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 
  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 
 146     public SwingSet2() {
 147         this(null);
 148     }
 149 
 150     /**
 151      * SwingSet2 Constructor
 152      */
 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      */
 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 
 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 {
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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 
 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      */
 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      */
 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      */
 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      */
 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      */
 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 }