1 /*
2  *
3  * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *   - Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  *
12  *   - Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *
16  *   - Neither the name of Oracle nor the names of its
17  *     contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 import javax.swing.*;
34 import javax.swing.event.*;
35 import javax.swing.border.*;
36 
37 import javax.swing.plaf.metal.MetalTheme;
38 import javax.swing.plaf.metal.OceanTheme;
39 import javax.swing.plaf.metal.DefaultMetalTheme;
40 import javax.swing.plaf.metal.MetalLookAndFeel;
41 
42 import java.lang.reflect.*;
43 import java.awt.*;
44 import java.awt.event.*;
45 import java.util.*;
46 
47 /**
48  * A demo that shows all of the Swing components.
49  *
50  * @author Jeff Dinkins
51  */
52 public class SwingSet2 extends JPanel {
53 
54     String[] demos = {
55       "ButtonDemo",
56       "ColorChooserDemo",
57       "ComboBoxDemo",
58       "FileChooserDemo",
59       "HtmlDemo",
60       "ListDemo",
61       "OptionPaneDemo",
62       "ProgressBarDemo",
63       "ScrollPaneDemo",
64       "SliderDemo",
65       "SplitPaneDemo",
66       "TabbedPaneDemo",
67       "TableDemo",
68       "ToolTipDemo",
69       "TreeDemo"
70     };
71 
loadDemos()72     void loadDemos() {
73         for(int i = 0; i < demos.length;) {
74             loadDemo(demos[i]);
75             i++;
76         }
77     }
78 
79     // The current Look & Feel
80     private static LookAndFeelData currentLookAndFeel;
81     private static LookAndFeelData[] lookAndFeelData;
82     // List of demos
83     private ArrayList<DemoModule> demosList = new ArrayList<DemoModule>();
84 
85     // The preferred size of the demo
86     private static final int PREFERRED_WIDTH = 720;
87     private static final int PREFERRED_HEIGHT = 640;
88 
89     // Box spacers
90     private Dimension HGAP = new Dimension(1,5);
91     private Dimension VGAP = new Dimension(5,1);
92 
93     // A place to hold on to the visible demo
94     private DemoModule currentDemo = null;
95     private JPanel demoPanel = null;
96 
97     // About Box
98     private JDialog aboutBox = null;
99 
100     // Status Bar
101     private JTextField statusField = null;
102 
103     // Tool Bar
104     private ToggleButtonToolBar toolbar = null;
105     private ButtonGroup toolbarGroup = new ButtonGroup();
106 
107     // Menus
108     private JMenuBar menuBar = null;
109     private JMenu lafMenu = null;
110     private JMenu themesMenu = null;
111     private JMenu audioMenu = null;
112     private JMenu optionsMenu = null;
113     private ButtonGroup lafMenuGroup = new ButtonGroup();
114     private ButtonGroup themesMenuGroup = new ButtonGroup();
115     private ButtonGroup audioMenuGroup = new ButtonGroup();
116 
117     // Popup menu
118     private JPopupMenu popupMenu = null;
119     private ButtonGroup popupMenuGroup = new ButtonGroup();
120 
121     // Used only if swingset is an application
122     private JFrame frame = null;
123 
124     // To debug or not to debug, that is the question
125     private boolean DEBUG = true;
126     private int debugCounter = 0;
127 
128     // The tab pane that holds the demo
129     private JTabbedPane tabbedPane = null;
130 
131     private JEditorPane demoSrcPane = null;
132 
133 
134     // contentPane cache, saved from the applet or application frame
135     Container contentPane = null;
136 
137 
138     // number of swingsets - for multiscreen
139     // keep track of the number of SwingSets created - we only want to exit
140     // the program when the last one has been closed.
141     private static int numSSs = 0;
142     private static Vector<SwingSet2> swingSets = new Vector<SwingSet2>();
143 
144     private boolean dragEnabled = false;
145 
SwingSet2()146     public SwingSet2() {
147         this(null);
148     }
149 
150     /**
151      * SwingSet2 Constructor
152      */
SwingSet2(GraphicsConfiguration gc)153     public SwingSet2(GraphicsConfiguration gc) {
154         String lafClassName = UIManager.getLookAndFeel().getClass().getName();
155         lookAndFeelData = getInstalledLookAndFeelData();
156         currentLookAndFeel = Arrays.stream(lookAndFeelData)
157                 .filter(laf -> lafClassName.equals(laf.className))
158                 .findFirst().get();
159 
160         frame = createFrame(gc);
161 
162         // set the layout
163         setLayout(new BorderLayout());
164 
165         // set the preferred size of the demo
166         setPreferredSize(new Dimension(PREFERRED_WIDTH,PREFERRED_HEIGHT));
167 
168         initializeDemo();
169         preloadFirstDemo();
170 
171         showSwingSet2();
172 
173         // Start loading the rest of the demo in the background
174         DemoLoadThread demoLoader = new DemoLoadThread(this);
175         demoLoader.start();
176     }
177 
178 
179     /**
180      * SwingSet2 Main. Called only if we're an application, not an applet.
181      */
main(final String[] args)182     public static void main(final String[] args) {
183         // must run in EDT when constructing the GUI components
184         SwingUtilities.invokeLater(() -> {
185             // Create SwingSet on the default monitor
186             UIManager.put("swing.boldMetal", Boolean.FALSE);
187             SwingSet2 swingset = new SwingSet2(GraphicsEnvironment.
188                                          getLocalGraphicsEnvironment().
189                                          getDefaultScreenDevice().
190                                          getDefaultConfiguration());
191         });
192     }
193 
194     // *******************************************************
195     // *************** Demo Loading Methods ******************
196     // *******************************************************
197 
198 
199 
initializeDemo()200     public void initializeDemo() {
201         JPanel top = new JPanel();
202         top.setLayout(new BorderLayout());
203         add(top, BorderLayout.NORTH);
204 
205         menuBar = createMenus();
206 
207         frame.setJMenuBar(menuBar);
208 
209         // creates popup menu accessible via keyboard
210         popupMenu = createPopupMenu();
211 
212         ToolBarPanel toolbarPanel = new ToolBarPanel();
213         toolbarPanel.setLayout(new BorderLayout());
214         toolbar = new ToggleButtonToolBar();
215         toolbarPanel.add(toolbar, BorderLayout.CENTER);
216         top.add(toolbarPanel, BorderLayout.SOUTH);
217         toolbarPanel.addContainerListener(toolbarPanel);
218 
219         tabbedPane = new JTabbedPane();
220         add(tabbedPane, BorderLayout.CENTER);
221         tabbedPane.getModel().addChangeListener(new TabListener());
222 
223         statusField = new JTextField("");
224         statusField.setEditable(false);
225         add(statusField, BorderLayout.SOUTH);
226 
227         demoPanel = new JPanel();
228         demoPanel.setLayout(new BorderLayout());
229         demoPanel.setBorder(new EtchedBorder());
230         tabbedPane.addTab("Hi There!", demoPanel);
231 
232         // Add html src code viewer
233         demoSrcPane = new JEditorPane("text/html", getString("SourceCode.loading"));
234         demoSrcPane.setEditable(false);
235 
236         JScrollPane scroller = new JScrollPane();
237         scroller.getViewport().add(demoSrcPane);
238 
239         tabbedPane.addTab(
240             getString("TabbedPane.src_label"),
241             null,
242             scroller,
243             getString("TabbedPane.src_tooltip")
244         );
245     }
246 
247     DemoModule currentTabDemo = null;
248     class TabListener implements ChangeListener {
stateChanged(ChangeEvent e)249         public void stateChanged(ChangeEvent e) {
250             SingleSelectionModel model = (SingleSelectionModel) e.getSource();
251             boolean srcSelected = model.getSelectedIndex() == 1;
252             if(currentTabDemo != currentDemo && demoSrcPane != null && srcSelected) {
253                 demoSrcPane.setText(getString("SourceCode.loading"));
254                 repaint();
255             }
256             if(currentTabDemo != currentDemo && srcSelected) {
257                 currentTabDemo = currentDemo;
258                 setSourceCode(currentDemo);
259             }
260         }
261     }
262 
263 
264     /**
265      * Create menus
266      */
createMenus()267     public JMenuBar createMenus() {
268         JMenuItem mi;
269         // ***** create the menubar ****
270         JMenuBar menuBar = new JMenuBar();
271         menuBar.getAccessibleContext().setAccessibleName(
272             getString("MenuBar.accessible_description"));
273 
274         // ***** create File menu
275         JMenu fileMenu = (JMenu) menuBar.add(new JMenu(getString("FileMenu.file_label")));
276         fileMenu.setMnemonic(getMnemonic("FileMenu.file_mnemonic"));
277         fileMenu.getAccessibleContext().setAccessibleDescription(getString("FileMenu.accessible_description"));
278 
279         createMenuItem(fileMenu, "FileMenu.about_label", "FileMenu.about_mnemonic",
280                        "FileMenu.about_accessible_description", new AboutAction(this));
281 
282         fileMenu.addSeparator();
283 
284         createMenuItem(fileMenu, "FileMenu.open_label", "FileMenu.open_mnemonic",
285                        "FileMenu.open_accessible_description", null);
286 
287         createMenuItem(fileMenu, "FileMenu.save_label", "FileMenu.save_mnemonic",
288                        "FileMenu.save_accessible_description", null);
289 
290         createMenuItem(fileMenu, "FileMenu.save_as_label", "FileMenu.save_as_mnemonic",
291                        "FileMenu.save_as_accessible_description", null);
292 
293 
294         fileMenu.addSeparator();
295 
296         createMenuItem(fileMenu, "FileMenu.exit_label", "FileMenu.exit_mnemonic",
297                        "FileMenu.exit_accessible_description", new ExitAction(this)
298         );
299 
300         // Create these menu items for the first SwingSet only.
301         if (numSSs == 0) {
302         // ***** create laf switcher menu
303         lafMenu = (JMenu) menuBar.add(new JMenu(getString("LafMenu.laf_label")));
304         lafMenu.setMnemonic(getMnemonic("LafMenu.laf_mnemonic"));
305         lafMenu.getAccessibleContext().setAccessibleDescription(
306             getString("LafMenu.laf_accessible_description"));
307 
308         for (LookAndFeelData lafData : lookAndFeelData) {
309             mi = createLafMenuItem(lafMenu, lafData);
310             mi.setSelected(lafData.equals(currentLookAndFeel));
311         }
312 
313         // ***** create themes menu
314         themesMenu = (JMenu) menuBar.add(new JMenu(getString("ThemesMenu.themes_label")));
315         themesMenu.setMnemonic(getMnemonic("ThemesMenu.themes_mnemonic"));
316         themesMenu.getAccessibleContext().setAccessibleDescription(
317             getString("ThemesMenu.themes_accessible_description"));
318 
319         // ***** create the audio submenu under the theme menu
320         audioMenu = (JMenu) themesMenu.add(new JMenu(getString("AudioMenu.audio_label")));
321         audioMenu.setMnemonic(getMnemonic("AudioMenu.audio_mnemonic"));
322         audioMenu.getAccessibleContext().setAccessibleDescription(
323             getString("AudioMenu.audio_accessible_description"));
324 
325         createAudioMenuItem(audioMenu, "AudioMenu.on_label",
326                             "AudioMenu.on_mnemonic",
327                             "AudioMenu.on_accessible_description",
328                             new OnAudioAction(this));
329 
330         mi = createAudioMenuItem(audioMenu, "AudioMenu.default_label",
331                                  "AudioMenu.default_mnemonic",
332                                  "AudioMenu.default_accessible_description",
333                                  new DefaultAudioAction(this));
334         mi.setSelected(true); // This is the default feedback setting
335 
336         createAudioMenuItem(audioMenu, "AudioMenu.off_label",
337                             "AudioMenu.off_mnemonic",
338                             "AudioMenu.off_accessible_description",
339                             new OffAudioAction(this));
340 
341 
342         // ***** create the font submenu under the theme menu
343         JMenu fontMenu = (JMenu) themesMenu.add(new JMenu(getString("FontMenu.fonts_label")));
344         fontMenu.setMnemonic(getMnemonic("FontMenu.fonts_mnemonic"));
345         fontMenu.getAccessibleContext().setAccessibleDescription(
346             getString("FontMenu.fonts_accessible_description"));
347         ButtonGroup fontButtonGroup = new ButtonGroup();
348         mi = createButtonGroupMenuItem(fontMenu, "FontMenu.plain_label",
349                 "FontMenu.plain_mnemonic",
350                 "FontMenu.plain_accessible_description",
351                 new ChangeFontAction(this, true), fontButtonGroup);
352         mi.setSelected(true);
353         mi = createButtonGroupMenuItem(fontMenu, "FontMenu.bold_label",
354                 "FontMenu.bold_mnemonic",
355                 "FontMenu.bold_accessible_description",
356                 new ChangeFontAction(this, false), fontButtonGroup);
357 
358 
359 
360         // *** now back to adding color/font themes to the theme menu
361         mi = createThemesMenuItem(themesMenu, "ThemesMenu.ocean_label",
362                                               "ThemesMenu.ocean_mnemonic",
363                                               "ThemesMenu.ocean_accessible_description",
364                                               new OceanTheme());
365         mi.setSelected(true); // This is the default theme
366 
367         createThemesMenuItem(themesMenu, "ThemesMenu.steel_label",
368                              "ThemesMenu.steel_mnemonic",
369                              "ThemesMenu.steel_accessible_description",
370                              new DefaultMetalTheme());
371 
372         createThemesMenuItem(themesMenu, "ThemesMenu.aqua_label", "ThemesMenu.aqua_mnemonic",
373                        "ThemesMenu.aqua_accessible_description", new AquaTheme());
374 
375         createThemesMenuItem(themesMenu, "ThemesMenu.charcoal_label", "ThemesMenu.charcoal_mnemonic",
376                        "ThemesMenu.charcoal_accessible_description", new CharcoalTheme());
377 
378         createThemesMenuItem(themesMenu, "ThemesMenu.contrast_label", "ThemesMenu.contrast_mnemonic",
379                        "ThemesMenu.contrast_accessible_description", new ContrastTheme());
380 
381         createThemesMenuItem(themesMenu, "ThemesMenu.emerald_label", "ThemesMenu.emerald_mnemonic",
382                        "ThemesMenu.emerald_accessible_description", new EmeraldTheme());
383 
384         createThemesMenuItem(themesMenu, "ThemesMenu.ruby_label", "ThemesMenu.ruby_mnemonic",
385                        "ThemesMenu.ruby_accessible_description", new RubyTheme());
386 
387         // Enable theme menu based on L&F
388         themesMenu.setEnabled("Metal".equals(currentLookAndFeel.name));
389 
390         // ***** create the options menu
391         optionsMenu = (JMenu)menuBar.add(
392             new JMenu(getString("OptionsMenu.options_label")));
393         optionsMenu.setMnemonic(getMnemonic("OptionsMenu.options_mnemonic"));
394         optionsMenu.getAccessibleContext().setAccessibleDescription(
395             getString("OptionsMenu.options_accessible_description"));
396 
397         // ***** create tool tip submenu item.
398         mi = createCheckBoxMenuItem(optionsMenu, "OptionsMenu.tooltip_label",
399                 "OptionsMenu.tooltip_mnemonic",
400                 "OptionsMenu.tooltip_accessible_description",
401                 new ToolTipAction());
402         mi.setSelected(true);
403 
404         // ***** create drag support submenu item.
405         createCheckBoxMenuItem(optionsMenu, "OptionsMenu.dragEnabled_label",
406                 "OptionsMenu.dragEnabled_mnemonic",
407                 "OptionsMenu.dragEnabled_accessible_description",
408                 new DragSupportAction());
409 
410         }
411 
412 
413         // ***** create the multiscreen menu, if we have multiple screens
414         GraphicsDevice[] screens = GraphicsEnvironment.
415                                     getLocalGraphicsEnvironment().
416                                     getScreenDevices();
417         if (screens.length > 1) {
418 
419             JMenu multiScreenMenu = (JMenu) menuBar.add(new JMenu(
420                                      getString("MultiMenu.multi_label")));
421 
422             multiScreenMenu.setMnemonic(getMnemonic("MultiMenu.multi_mnemonic"));
423             multiScreenMenu.getAccessibleContext().setAccessibleDescription(
424              getString("MultiMenu.multi_accessible_description"));
425 
426             createMultiscreenMenuItem(multiScreenMenu, MultiScreenAction.ALL_SCREENS);
427             for (int i = 0; i < screens.length; i++) {
428                 createMultiscreenMenuItem(multiScreenMenu, i);
429             }
430         }
431 
432         return menuBar;
433     }
434 
435     /**
436      * Create a checkbox menu menu item
437      */
createCheckBoxMenuItem(JMenu menu, String label, String mnemonic, String accessibleDescription, Action action)438     private JMenuItem createCheckBoxMenuItem(JMenu menu, String label,
439                                              String mnemonic,
440                                              String accessibleDescription,
441                                              Action action) {
442         JCheckBoxMenuItem mi = (JCheckBoxMenuItem)menu.add(
443                 new JCheckBoxMenuItem(getString(label)));
444         mi.setMnemonic(getMnemonic(mnemonic));
445         mi.getAccessibleContext().setAccessibleDescription(getString(
446                 accessibleDescription));
447         mi.addActionListener(action);
448         return mi;
449     }
450 
451     /**
452      * Create a radio button menu menu item for items that are part of a
453      * button group.
454      */
createButtonGroupMenuItem(JMenu menu, String label, String mnemonic, String accessibleDescription, Action action, ButtonGroup buttonGroup)455     private JMenuItem createButtonGroupMenuItem(JMenu menu, String label,
456                                                 String mnemonic,
457                                                 String accessibleDescription,
458                                                 Action action,
459                                                 ButtonGroup buttonGroup) {
460         JRadioButtonMenuItem mi = (JRadioButtonMenuItem)menu.add(
461                 new JRadioButtonMenuItem(getString(label)));
462         buttonGroup.add(mi);
463         mi.setMnemonic(getMnemonic(mnemonic));
464         mi.getAccessibleContext().setAccessibleDescription(getString(
465                 accessibleDescription));
466         mi.addActionListener(action);
467         return mi;
468     }
469 
470     /**
471      * Create the theme's audio submenu
472      */
createAudioMenuItem(JMenu menu, String label, String mnemonic, String accessibleDescription, Action action)473     public JMenuItem createAudioMenuItem(JMenu menu, String label,
474                                          String mnemonic,
475                                          String accessibleDescription,
476                                          Action action) {
477         JRadioButtonMenuItem mi = (JRadioButtonMenuItem) menu.add(new JRadioButtonMenuItem(getString(label)));
478         audioMenuGroup.add(mi);
479         mi.setMnemonic(getMnemonic(mnemonic));
480         mi.getAccessibleContext().setAccessibleDescription(getString(accessibleDescription));
481         mi.addActionListener(action);
482 
483         return mi;
484     }
485 
486     /**
487      * Creates a generic menu item
488      */
createMenuItem(JMenu menu, String label, String mnemonic, String accessibleDescription, Action action)489     public JMenuItem createMenuItem(JMenu menu, String label, String mnemonic,
490                                String accessibleDescription, Action action) {
491         JMenuItem mi = (JMenuItem) menu.add(new JMenuItem(getString(label)));
492         mi.setMnemonic(getMnemonic(mnemonic));
493         mi.getAccessibleContext().setAccessibleDescription(getString(accessibleDescription));
494         mi.addActionListener(action);
495         if(action == null) {
496             mi.setEnabled(false);
497         }
498         return mi;
499     }
500 
501     /**
502      * Creates a JRadioButtonMenuItem for the Themes menu
503      */
createThemesMenuItem(JMenu menu, String label, String mnemonic, String accessibleDescription, MetalTheme theme)504     public JMenuItem createThemesMenuItem(JMenu menu, String label, String mnemonic,
505                                String accessibleDescription, MetalTheme theme) {
506         JRadioButtonMenuItem mi = (JRadioButtonMenuItem) menu.add(new JRadioButtonMenuItem(getString(label)));
507         themesMenuGroup.add(mi);
508         mi.setMnemonic(getMnemonic(mnemonic));
509         mi.getAccessibleContext().setAccessibleDescription(getString(accessibleDescription));
510         mi.addActionListener(new ChangeThemeAction(this, theme));
511 
512         return mi;
513     }
514 
515     /**
516      * Creates a JRadioButtonMenuItem for the Look and Feel menu
517      */
createLafMenuItem(JMenu menu, LookAndFeelData lafData)518     public JMenuItem createLafMenuItem(JMenu menu, LookAndFeelData lafData) {
519         JMenuItem mi = menu.add(new JRadioButtonMenuItem(lafData.label));
520         lafMenuGroup.add(mi);
521         mi.setMnemonic(lafData.mnemonic);
522         mi.getAccessibleContext().setAccessibleDescription(lafData.accDescription);
523         mi.addActionListener(new ChangeLookAndFeelAction(this, lafData));
524         return mi;
525     }
526 
527     /**
528      * Creates a multi-screen menu item
529      */
createMultiscreenMenuItem(JMenu menu, int screen)530     public JMenuItem createMultiscreenMenuItem(JMenu menu, int screen) {
531         JMenuItem mi = null;
532         if (screen == MultiScreenAction.ALL_SCREENS) {
533             mi = (JMenuItem) menu.add(new JMenuItem(getString("MultiMenu.all_label")));
534             mi.setMnemonic(getMnemonic("MultiMenu.all_mnemonic"));
535             mi.getAccessibleContext().setAccessibleDescription(getString(
536                                                                  "MultiMenu.all_accessible_description"));
537         }
538         else {
539             mi = (JMenuItem) menu.add(new JMenuItem(getString("MultiMenu.single_label") + " " +
540                                                                                                  screen));
541             mi.setMnemonic(KeyEvent.VK_0 + screen);
542             mi.getAccessibleContext().setAccessibleDescription(getString(
543                                                "MultiMenu.single_accessible_description") + " " + screen);
544 
545         }
546         mi.addActionListener(new MultiScreenAction(this, screen));
547         return mi;
548     }
549 
createPopupMenu()550     public JPopupMenu createPopupMenu() {
551         JPopupMenu popup = new JPopupMenu("JPopupMenu demo");
552 
553         for (LookAndFeelData lafData : lookAndFeelData) {
554             createPopupMenuItem(popup, lafData);
555         }
556 
557         // register key binding to activate popup menu
558         InputMap map = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
559         map.put(KeyStroke.getKeyStroke(KeyEvent.VK_F10, InputEvent.SHIFT_DOWN_MASK),
560                 "postMenuAction");
561         map.put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTEXT_MENU, 0), "postMenuAction");
562         getActionMap().put("postMenuAction", new ActivatePopupMenuAction(this, popup));
563 
564         return popup;
565     }
566 
567     /**
568      * Creates a JMenuItem for the Look and Feel popup menu
569      */
createPopupMenuItem(JPopupMenu menu, LookAndFeelData lafData)570     public JMenuItem createPopupMenuItem(JPopupMenu menu, LookAndFeelData lafData) {
571         JMenuItem mi = menu.add(new JMenuItem(lafData.label));
572         popupMenuGroup.add(mi);
573         mi.setMnemonic(lafData.mnemonic);
574         mi.getAccessibleContext().setAccessibleDescription(lafData.accDescription);
575         mi.addActionListener(new ChangeLookAndFeelAction(this, lafData));
576         return mi;
577     }
578 
579 
580     /**
581      * Load the first demo. This is done separately from the remaining demos
582      * so that we can get SwingSet2 up and available to the user quickly.
583      */
preloadFirstDemo()584     public void preloadFirstDemo() {
585         DemoModule demo = addDemo(new InternalFrameDemo(this));
586         setDemo(demo);
587     }
588 
589 
590     /**
591      * Add a demo to the toolbar
592      */
addDemo(DemoModule demo)593     public DemoModule addDemo(DemoModule demo) {
594         demosList.add(demo);
595         if (dragEnabled) {
596             demo.updateDragEnabled(true);
597         }
598         // do the following on the gui thread
599         SwingUtilities.invokeLater(new SwingSetRunnable(this, demo) {
600             public void run() {
601                 SwitchToDemoAction action = new SwitchToDemoAction(swingset, (DemoModule) obj);
602                 JToggleButton tb = swingset.getToolBar().addToggleButton(action);
603                 swingset.getToolBarGroup().add(tb);
604                 if(swingset.getToolBarGroup().getSelection() == null) {
605                     tb.setSelected(true);
606                 }
607                 tb.setText(null);
608                 tb.setToolTipText(((DemoModule)obj).getToolTip());
609 
610                 if(demos[demos.length-1].equals(obj.getClass().getName())) {
611                     setStatus(getString("Status.popupMenuAccessible"));
612                 }
613 
614             }
615         });
616         return demo;
617     }
618 
619 
620     /**
621      * Sets the current demo
622      */
setDemo(DemoModule demo)623     public void setDemo(DemoModule demo) {
624         currentDemo = demo;
625 
626         // Ensure panel's UI is current before making visible
627         JComponent currentDemoPanel = demo.getDemoPanel();
628         SwingUtilities.updateComponentTreeUI(currentDemoPanel);
629 
630         demoPanel.removeAll();
631         demoPanel.add(currentDemoPanel, BorderLayout.CENTER);
632 
633         tabbedPane.setSelectedIndex(0);
634         tabbedPane.setTitleAt(0, demo.getName());
635         tabbedPane.setToolTipTextAt(0, demo.getToolTip());
636     }
637 
638 
639     /**
640      * Bring up the SwingSet2 demo by showing the frame
641      */
showSwingSet2()642     public void showSwingSet2() {
643         // put swingset in a frame and show it
644         JFrame f = getFrame();
645         f.setTitle(getString("Frame.title"));
646         f.getContentPane().add(this, BorderLayout.CENTER);
647         f.pack();
648 
649         Rectangle screenRect = f.getGraphicsConfiguration().getBounds();
650         Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(
651                 f.getGraphicsConfiguration());
652 
653         // Make sure we don't place the demo off the screen.
654         int centerWidth = screenRect.width < f.getSize().width ?
655                 screenRect.x :
656                 screenRect.x + screenRect.width/2 - f.getSize().width/2;
657         int centerHeight = screenRect.height < f.getSize().height ?
658                 screenRect.y :
659                 screenRect.y + screenRect.height/2 - f.getSize().height/2;
660 
661         centerHeight = centerHeight < screenInsets.top ?
662                 screenInsets.top : centerHeight;
663 
664         f.setLocation(centerWidth, centerHeight);
665         f.setVisible(true);
666         numSSs++;
667         swingSets.add(this);
668     }
669 
670     // *******************************************************
671     // ****************** Utility Methods ********************
672     // *******************************************************
673 
674     /**
675      * Loads a demo from a classname
676      */
677     void loadDemo(String classname) {
678         setStatus(getString("Status.loading") + getString(classname + ".name"));
679         DemoModule demo = null;
680         try {
681             Class<?> demoClass = Class.forName(classname);
682             Constructor<?> demoConstructor = demoClass.getConstructor(new Class[]{SwingSet2.class});
683             demo = (DemoModule) demoConstructor.newInstance(new Object[]{this});
684             addDemo(demo);
685         } catch (Exception e) {
686             System.out.println("Error occurred loading demo: " + classname);
687         }
688     }
689 
690     /**
691      * Returns the frame instance
692      */
693     public JFrame getFrame() {
694         return frame;
695     }
696 
697     /**
698      * Returns the menubar
699      */
700     public JMenuBar getMenuBar() {
701         return menuBar;
702     }
703 
704     /**
705      * Returns the toolbar
706      */
707     public ToggleButtonToolBar getToolBar() {
708         return toolbar;
709     }
710 
711     /**
712      * Returns the toolbar button group
713      */
714     public ButtonGroup getToolBarGroup() {
715         return toolbarGroup;
716     }
717 
718     /**
719      * Returns the content pane whether we're in an applet
720      * or application
721      */
722     public Container getContentPane() {
723         if(contentPane == null) {
724             if(getFrame() != null) {
725                 contentPane = getFrame().getContentPane();
726             }
727         }
728         return contentPane;
729     }
730 
731     /**
732      * Create a frame for SwingSet2 to reside in if brought up
733      * as an application.
734      */
735     public static JFrame createFrame(GraphicsConfiguration gc) {
736         JFrame frame = new JFrame(gc);
737         if (numSSs == 0) {
738             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
739         } else {
740             WindowListener l = new WindowAdapter() {
741                 public void windowClosing(WindowEvent e) {
742                     numSSs--;
743                     swingSets.remove(this);
744                 }
745             };
746             frame.addWindowListener(l);
747         }
748         return frame;
749     }
750 
751 
752     /**
753      * Set the status
754      */
755     public void setStatus(String s) {
756         // do the following on the gui thread
757         SwingUtilities.invokeLater(new SwingSetRunnable(this, s) {
758             public void run() {
759                 swingset.statusField.setText((String) obj);
760             }
761         });
762     }
763 
764 
765     /**
766      * This method returns a string from the demo's resource bundle.
767      */
768     public static String getString(String key) {
769         String value = null;
770         try {
771             value = TextAndMnemonicUtils.getTextAndMnemonicString(key);
772         } catch (MissingResourceException e) {
773             System.out.println("java.util.MissingResourceException: Couldn't find value for: " + key);
774         }
775         if(value == null) {
776             value = "Could not find resource: " + key + "  ";
777         }
778         return value;
779     }
780 
781     void setDragEnabled(boolean dragEnabled) {
782         if (dragEnabled == this.dragEnabled) {
783             return;
784         }
785 
786         this.dragEnabled = dragEnabled;
787 
788         for (DemoModule dm : demosList) {
789             dm.updateDragEnabled(dragEnabled);
790         }
791 
792         demoSrcPane.setDragEnabled(dragEnabled);
793     }
794 
795     boolean isDragEnabled() {
796         return dragEnabled;
797     }
798 
799 
800     /**
801      * Returns a mnemonic from the resource bundle. Typically used as
802      * keyboard shortcuts in menu items.
803      */
804     public char getMnemonic(String key) {
805         return (getString(key)).charAt(0);
806     }
807 
808     /**
809      * Creates an icon from an image contained in the "images" directory.
810      */
811     public ImageIcon createImageIcon(String filename, String description) {
812         String path = "/resources/images/" + filename;
813         return new ImageIcon(getClass().getResource(path));
814     }
815 
816     /**
817      * If DEBUG is defined, prints debug information out to std ouput.
818      */
819     public void debug(String s) {
820         if(DEBUG) {
821             System.out.println((debugCounter++) + ": " + s);
822         }
823     }
824 
825     /**
826      * Stores the current L&F, and calls updateLookAndFeel, below
827      */
828     public void setLookAndFeel(LookAndFeelData laf) {
829         if(!currentLookAndFeel.equals(laf)) {
830             currentLookAndFeel = laf;
831             /* The recommended way of synchronizing state between multiple
832              * controls that represent the same command is to use Actions.
833              * The code below is a workaround and will be replaced in future
834              * version of SwingSet2 demo.
835              */
836             String lafName = laf.label;
837             themesMenu.setEnabled(laf.name.equals("Metal"));
838             updateLookAndFeel();
839             for(int i=0;i<lafMenu.getItemCount();i++) {
840                 JMenuItem item = lafMenu.getItem(i);
841                 item.setSelected(item.getText().equals(lafName));
842             }
843         }
844     }
845 
846     private void updateThisSwingSet() {
847         JFrame frame = getFrame();
848         if (frame == null) {
849             SwingUtilities.updateComponentTreeUI(this);
850         } else {
851             SwingUtilities.updateComponentTreeUI(frame);
852         }
853 
854         SwingUtilities.updateComponentTreeUI(popupMenu);
855         if (aboutBox != null) {
856             SwingUtilities.updateComponentTreeUI(aboutBox);
857         }
858     }
859 
860     /**
861      * Sets the current L&F on each demo module
862      */
863     public void updateLookAndFeel() {
864         try {
865             UIManager.setLookAndFeel(currentLookAndFeel.className);
866             for (SwingSet2 ss : swingSets) {
867                 ss.updateThisSwingSet();
868             }
869         } catch (Exception ex) {
870             System.out.println("Failed loading L&F: " + currentLookAndFeel);
871             System.out.println(ex);
872         }
873     }
874 
875     /**
876      * Loads and puts the source code text into JEditorPane in the "Source Code" tab
877      */
878     public void setSourceCode(DemoModule demo) {
879         // do the following on the gui thread
880         SwingUtilities.invokeLater(new SwingSetRunnable(this, demo) {
881             public void run() {
882                 swingset.demoSrcPane.setText(((DemoModule)obj).getSourceCode());
883                 swingset.demoSrcPane.setCaretPosition(0);
884 
885             }
886         });
887     }
888 
889     // *******************************************************
890     // **************   ToggleButtonToolbar  *****************
891     // *******************************************************
892     static Insets zeroInsets = new Insets(1,1,1,1);
893     protected class ToggleButtonToolBar extends JToolBar {
894         public ToggleButtonToolBar() {
895             super();
896         }
897 
898         JToggleButton addToggleButton(Action a) {
899             JToggleButton tb = new JToggleButton(
900                 (String)a.getValue(Action.NAME),
901                 (Icon)a.getValue(Action.SMALL_ICON)
902             );
903             tb.setMargin(zeroInsets);
904             tb.setText(null);
905             tb.setEnabled(a.isEnabled());
906             tb.setToolTipText((String)a.getValue(Action.SHORT_DESCRIPTION));
907             tb.setAction(a);
908             add(tb);
909             return tb;
910         }
911     }
912 
913     // *******************************************************
914     // *********  ToolBar Panel / Docking Listener ***********
915     // *******************************************************
916     class ToolBarPanel extends JPanel implements ContainerListener {
917 
918         public boolean contains(int x, int y) {
919             Component c = getParent();
920             if (c != null) {
921                 Rectangle r = c.getBounds();
922                 return (x >= 0) && (x < r.width) && (y >= 0) && (y < r.height);
923             }
924             else {
925                 return super.contains(x,y);
926             }
927         }
928 
929         public void componentAdded(ContainerEvent e) {
930             Container c = e.getContainer().getParent();
931             if (c != null) {
932                 c.getParent().validate();
933                 c.getParent().repaint();
934             }
935         }
936 
937         public void componentRemoved(ContainerEvent e) {
938             Container c = e.getContainer().getParent();
939             if (c != null) {
940                 c.getParent().validate();
941                 c.getParent().repaint();
942             }
943         }
944     }
945 
946     // *******************************************************
947     // ******************   Runnables  ***********************
948     // *******************************************************
949 
950     /**
951      * Generic SwingSet2 runnable. This is intended to run on the
952      * AWT gui event thread so as not to muck things up by doing
953      * gui work off the gui thread. Accepts a SwingSet2 and an Object
954      * as arguments, which gives subtypes of this class the two
955      * "must haves" needed in most runnables for this demo.
956      */
957     class SwingSetRunnable implements Runnable {
958         protected SwingSet2 swingset;
959         protected Object obj;
960 
961         public SwingSetRunnable(SwingSet2 swingset, Object obj) {
962             this.swingset = swingset;
963             this.obj = obj;
964         }
965 
966         public void run() {
967         }
968     }
969 
970 
971     // *******************************************************
972     // ********************   Actions  ***********************
973     // *******************************************************
974 
975     public class SwitchToDemoAction extends AbstractAction {
976         SwingSet2 swingset;
977         DemoModule demo;
978 
979         public SwitchToDemoAction(SwingSet2 swingset, DemoModule demo) {
980             super(demo.getName(), demo.getIcon());
981             this.swingset = swingset;
982             this.demo = demo;
983         }
984 
985         public void actionPerformed(ActionEvent e) {
986             swingset.setDemo(demo);
987         }
988     }
989 
990     class OkAction extends AbstractAction {
991         JDialog aboutBox;
992 
993         protected OkAction(JDialog aboutBox) {
994             super("OkAction");
995             this.aboutBox = aboutBox;
996         }
997 
998         public void actionPerformed(ActionEvent e) {
999             aboutBox.setVisible(false);
1000         }
1001     }
1002 
1003     class ChangeLookAndFeelAction extends AbstractAction {
1004         SwingSet2 swingset;
1005         LookAndFeelData lafData;
1006         protected ChangeLookAndFeelAction(SwingSet2 swingset, LookAndFeelData lafData) {
1007             super("ChangeTheme");
1008             this.swingset = swingset;
1009             this.lafData = lafData;
1010         }
1011 
1012         public void actionPerformed(ActionEvent e) {
1013             swingset.setLookAndFeel(lafData);
1014         }
1015     }
1016 
1017     class ActivatePopupMenuAction extends AbstractAction {
1018         SwingSet2 swingset;
1019         JPopupMenu popup;
1020         protected ActivatePopupMenuAction(SwingSet2 swingset, JPopupMenu popup) {
1021             super("ActivatePopupMenu");
1022             this.swingset = swingset;
1023             this.popup = popup;
1024         }
1025 
1026         public void actionPerformed(ActionEvent e) {
1027             Dimension invokerSize = getSize();
1028             Dimension popupSize = popup.getPreferredSize();
1029             popup.show(swingset, (invokerSize.width - popupSize.width) / 2,
1030                        (invokerSize.height - popupSize.height) / 2);
1031         }
1032     }
1033 
1034     // Turns on all possible auditory feedback
1035     class OnAudioAction extends AbstractAction {
1036         SwingSet2 swingset;
1037         protected OnAudioAction(SwingSet2 swingset) {
1038             super("Audio On");
1039             this.swingset = swingset;
1040         }
1041         public void actionPerformed(ActionEvent e) {
1042             UIManager.put("AuditoryCues.playList",
1043                           UIManager.get("AuditoryCues.allAuditoryCues"));
1044             swingset.updateLookAndFeel();
1045         }
1046     }
1047 
1048     // Turns on the default amount of auditory feedback
1049     class DefaultAudioAction extends AbstractAction {
1050         SwingSet2 swingset;
1051         protected DefaultAudioAction(SwingSet2 swingset) {
1052             super("Audio Default");
1053             this.swingset = swingset;
1054         }
1055         public void actionPerformed(ActionEvent e) {
1056             UIManager.put("AuditoryCues.playList",
1057                           UIManager.get("AuditoryCues.defaultCueList"));
1058             swingset.updateLookAndFeel();
1059         }
1060     }
1061 
1062     // Turns off all possible auditory feedback
1063     class OffAudioAction extends AbstractAction {
1064         SwingSet2 swingset;
1065         protected OffAudioAction(SwingSet2 swingset) {
1066             super("Audio Off");
1067             this.swingset = swingset;
1068         }
1069         public void actionPerformed(ActionEvent e) {
1070             UIManager.put("AuditoryCues.playList",
1071                           UIManager.get("AuditoryCues.noAuditoryCues"));
1072             swingset.updateLookAndFeel();
1073         }
1074     }
1075 
1076     // Turns on or off the tool tips for the demo.
1077     class ToolTipAction extends AbstractAction {
1078         protected ToolTipAction() {
1079             super("ToolTip Control");
1080         }
1081 
1082         public void actionPerformed(ActionEvent e) {
1083             boolean status = ((JCheckBoxMenuItem)e.getSource()).isSelected();
1084             ToolTipManager.sharedInstance().setEnabled(status);
1085         }
1086     }
1087 
1088     class DragSupportAction extends AbstractAction {
1089         protected DragSupportAction() {
1090             super("DragSupport Control");
1091         }
1092 
1093         public void actionPerformed(ActionEvent e) {
1094             boolean dragEnabled = ((JCheckBoxMenuItem)e.getSource()).isSelected();
1095             for (SwingSet2 ss : swingSets) {
1096                 ss.setDragEnabled(dragEnabled);
1097             }
1098         }
1099     }
1100 
1101     class ChangeThemeAction extends AbstractAction {
1102         SwingSet2 swingset;
1103         MetalTheme theme;
1104         protected ChangeThemeAction(SwingSet2 swingset, MetalTheme theme) {
1105             super("ChangeTheme");
1106             this.swingset = swingset;
1107             this.theme = theme;
1108         }
1109 
1110         public void actionPerformed(ActionEvent e) {
1111             MetalLookAndFeel.setCurrentTheme(theme);
1112             swingset.updateLookAndFeel();
1113         }
1114     }
1115 
1116     class ExitAction extends AbstractAction {
1117         SwingSet2 swingset;
1118         protected ExitAction(SwingSet2 swingset) {
1119             super("ExitAction");
1120             this.swingset = swingset;
1121         }
1122 
1123         public void actionPerformed(ActionEvent e) {
1124             System.exit(0);
1125         }
1126     }
1127 
1128     class AboutAction extends AbstractAction {
1129         SwingSet2 swingset;
1130         protected AboutAction(SwingSet2 swingset) {
1131             super("AboutAction");
1132             this.swingset = swingset;
1133         }
1134 
1135         public void actionPerformed(ActionEvent e) {
1136             if(aboutBox == null) {
1137                 // JPanel panel = new JPanel(new BorderLayout());
1138                 JPanel panel = new AboutPanel(swingset);
1139                 panel.setLayout(new BorderLayout());
1140 
1141                 aboutBox = new JDialog(swingset.getFrame(), getString("AboutBox.title"), false);
1142                 aboutBox.setResizable(false);
1143                 aboutBox.getContentPane().add(panel, BorderLayout.CENTER);
1144 
1145                 // JButton button = new JButton(getString("AboutBox.ok_button_text"));
1146                 JPanel buttonpanel = new JPanel();
1147                 buttonpanel.setBorder(new javax.swing.border.EmptyBorder(0, 0, 3, 0));
1148                 buttonpanel.setOpaque(false);
1149                 JButton button = (JButton) buttonpanel.add(
1150                     new JButton(getString("AboutBox.ok_button_text"))
1151                 );
1152                 panel.add(buttonpanel, BorderLayout.SOUTH);
1153 
1154                 button.addActionListener(new OkAction(aboutBox));
1155             }
1156             aboutBox.pack();
1157             aboutBox.setLocationRelativeTo(getFrame());
1158             aboutBox.setVisible(true);
1159         }
1160     }
1161 
1162     class MultiScreenAction extends AbstractAction {
1163         static final int ALL_SCREENS = -1;
1164         int screen;
1165         protected MultiScreenAction(SwingSet2 swingset, int screen) {
1166             super("MultiScreenAction");
1167             this.screen = screen;
1168         }
1169 
1170         public void actionPerformed(ActionEvent e) {
1171             GraphicsDevice[] gds = GraphicsEnvironment.
1172                                    getLocalGraphicsEnvironment().
1173                                    getScreenDevices();
1174             if (screen == ALL_SCREENS) {
1175                 for (int i = 0; i < gds.length; i++) {
1176                     SwingSet2 swingset = new SwingSet2(
1177                                   gds[i].getDefaultConfiguration());
1178                     swingset.setDragEnabled(dragEnabled);
1179                 }
1180             }
1181             else {
1182                 SwingSet2 swingset = new SwingSet2(
1183                              gds[screen].getDefaultConfiguration());
1184                 swingset.setDragEnabled(dragEnabled);
1185             }
1186         }
1187     }
1188 
1189     // *******************************************************
1190     // **********************  Misc  *************************
1191     // *******************************************************
1192 
1193     class DemoLoadThread extends Thread {
1194         SwingSet2 swingset;
1195 
1196         DemoLoadThread(SwingSet2 swingset) {
1197             this.swingset = swingset;
1198         }
1199 
1200         public void run() {
1201             SwingUtilities.invokeLater(swingset::loadDemos);
1202         }
1203     }
1204 
1205     class AboutPanel extends JPanel {
1206         ImageIcon aboutimage = null;
1207         SwingSet2 swingset = null;
1208 
1209         public AboutPanel(SwingSet2 swingset) {
1210             this.swingset = swingset;
1211             aboutimage = swingset.createImageIcon("About.jpg", "AboutBox.accessible_description");
1212             setOpaque(false);
1213         }
1214 
1215         public void paint(Graphics g) {
1216             aboutimage.paintIcon(this, g, 0, 0);
1217             super.paint(g);
1218         }
1219 
1220         public Dimension getPreferredSize() {
1221             return new Dimension(aboutimage.getIconWidth(),
1222                                  aboutimage.getIconHeight());
1223         }
1224     }
1225 
1226 
1227     private class ChangeFontAction extends AbstractAction {
1228         private SwingSet2 swingset;
1229         private boolean plain;
1230 
1231         ChangeFontAction(SwingSet2 swingset, boolean plain) {
1232             super("FontMenu");
1233             this.swingset = swingset;
1234             this.plain = plain;
1235         }
1236 
1237         public void actionPerformed(ActionEvent e) {
1238             if (plain) {
1239                 UIManager.put("swing.boldMetal", Boolean.FALSE);
1240             }
1241             else {
1242                 UIManager.put("swing.boldMetal", Boolean.TRUE);
1243             }
1244             // Change the look and feel to force the settings to take effect.
1245             updateLookAndFeel();
1246         }
1247     }
1248 
1249     private static LookAndFeelData[] getInstalledLookAndFeelData() {
1250         return Arrays.stream(UIManager.getInstalledLookAndFeels())
1251                 .map(laf -> getLookAndFeelData(laf))
1252                 .toArray(LookAndFeelData[]::new);
1253     }
1254 
1255     private static LookAndFeelData getLookAndFeelData(
1256             UIManager.LookAndFeelInfo info) {
1257         switch (info.getName()) {
1258             case "Metal":
1259                 return new LookAndFeelData(info, "java");
1260             case "Nimbus":
1261                 return new LookAndFeelData(info, "nimbus");
1262             case "Windows":
1263                 return new LookAndFeelData(info, "windows");
1264             case "GTK+":
1265                 return new LookAndFeelData(info, "gtk");
1266             case "CDE/Motif":
1267                 return new LookAndFeelData(info, "motif");
1268             case "Mac OS X":
1269                 return new LookAndFeelData(info, "mac");
1270             default:
1271                 return new LookAndFeelData(info);
1272         }
1273     }
1274 
1275     private static class LookAndFeelData {
1276         String name;
1277         String className;
1278         String label;
1279         char mnemonic;
1280         String accDescription;
1281 
1282         public LookAndFeelData(UIManager.LookAndFeelInfo info) {
1283             this(info.getName(), info.getClassName(), info.getName(),
1284                  info.getName(), info.getName());
1285         }
1286 
1287         public LookAndFeelData(UIManager.LookAndFeelInfo info, String property) {
1288             this(info.getName(), info.getClassName(),
1289                     getString(String.format("LafMenu.%s_label", property)),
1290                     getString(String.format("LafMenu.%s_mnemonic", property)),
1291                     getString(String.format("LafMenu.%s_accessible_description",
1292                                     property)));
1293         }
1294 
1295         public LookAndFeelData(String name, String className, String label,
1296                                String mnemonic, String accDescription) {
1297             this.name = name;
1298             this.className = className;
1299             this.label = label;
1300             this.mnemonic = mnemonic.charAt(0);
1301             this.accDescription = accDescription;
1302         }
1303 
1304         @Override
1305         public String toString() {
1306             return className;
1307         }
1308     }
1309 }
1310