1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.tool;
  28 
  29 import java.awt.Component;
  30 import java.awt.Container;
  31 import java.awt.EventQueue;
  32 import java.awt.GraphicsEnvironment;
  33 import java.awt.Rectangle;
  34 import java.awt.event.ActionEvent;
  35 import java.awt.event.ActionListener;
  36 import java.awt.event.WindowAdapter;
  37 import java.awt.event.WindowEvent;
  38 import java.awt.event.KeyEvent;
  39 import java.io.FileNotFoundException;
  40 import java.util.Collection;
  41 import java.util.Iterator;
  42 import java.util.List;
  43 import java.util.Map;
  44 import java.util.Vector;
  45 import javax.accessibility.AccessibleContext;
  46 import javax.swing.Action;
  47 import javax.swing.JFrame;
  48 import javax.swing.JMenu;
  49 import javax.swing.JMenuBar;
  50 import javax.swing.JMenuItem;
  51 import javax.swing.JRootPane;
  52 import javax.swing.SwingUtilities;
  53 import javax.swing.event.MenuEvent;
  54 import javax.swing.event.MenuListener;
  55 import javax.swing.KeyStroke;
  56 import javax.swing.JComponent;
  57 
  58 import com.sun.javatest.tool.jthelp.ContextHelpManager;
  59 import com.sun.javatest.tool.jthelp.HelpBroker;
  60 import com.sun.javatest.util.ExitCount;
  61 import com.sun.javatest.util.I18NResourceBundle;
  62 import com.sun.javatest.util.PrefixMap;
  63 import com.sun.javatest.util.StringArray;
  64 import java.awt.GraphicsConfiguration;
  65 import java.awt.GraphicsDevice;
  66 
  67 /**
  68  * A deskview defines the abstract behavior of a style of desktop,
  69  * such as MDI, SDI, or tabbed.
  70  * @see Desktop
  71  */
  72 abstract class DeskView {
  73     /**
  74      * This exception is used to report problems while using a tool manager.
  75      */
  76     static class Fault extends Exception
  77     {
  78         /**
  79          * Create a Fault.
  80          * @param i18n A resource bundle in which to find the detail message.
  81          * @param s The key for the detail message.
  82          */
  83         Fault(I18NResourceBundle i18n, String s) {
  84             super(i18n.getString(s));
  85         }
  86 
  87         /**
  88          * Create a Fault.
  89          * @param i18n A resource bundle in which to find the detail message.
  90          * @param s The key for the detail message.
  91          * @param o An argument to be formatted with the detail message by
  92          * {@link java.text.MessageFormat#format}
  93          */
  94         Fault(I18NResourceBundle i18n, String s, Object o) {
  95             super(i18n.getString(s, o));
  96         }
  97 
  98         /**
  99          * Create a Fault.
 100          * @param i18n A resource bundle in which to find the detail message.
 101          * @param s The key for the detail message.
 102          * @param o An array of arguments to be formatted with the detail message by
 103          * {@link java.text.MessageFormat#format}
 104          */
 105         Fault(I18NResourceBundle i18n, String s, Object[] o) {
 106             super(i18n.getString(s, o));
 107         }
 108     }
 109 
 110     protected DeskView(Desktop desktop) {
 111         this.desktop = desktop;
 112         uif = new UIFactory(getClass(), desktop.getHelpBroker());
 113     }
 114 
 115     public Desktop getDesktop() {
 116         return desktop;
 117     }
 118 
 119     /**
 120      * Dispose of this desktop object, and resources it may use.
 121      * Subsequent call of access() will cause a new desktop object
 122      * to be created.
 123      * @see #access
 124      */
 125     public void dispose() {
 126         Tool[] tools = getTools();
 127         for (int i = 0; i < tools.length; i++)
 128             tools[i].dispose();
 129 
 130         /*
 131         if (this == theOne) {
 132             theOne = null;
 133             if (helpBroker != null && helpBroker.isDisplayed()) {
 134                 helpBroker.setDisplayed(false);
 135             }
 136         }
 137         */
 138     }
 139 
 140     /**
 141      * Check if the top level windows of the desktop are visible or not.
 142      * @return true if the top level windows are visible; otherwise, return false
 143      * @see #setVisible
 144      */
 145     public abstract boolean isVisible();
 146 
 147     /**
 148      * Set whether or not the top level windows of the desktop should be visible.
 149      * @param v If true, the top level windows will be made visible; if false, they
 150      * will be hidden.
 151      */
 152     public abstract void setVisible(boolean v);
 153 
 154     /**
 155      * Check whether the desktop is empty of any tools.
 156      * @return true if there are no tools on the desktop, and false otherwise
 157      */
 158     public abstract boolean isEmpty();
 159 
 160     /**
 161      * Get the set of tools currently on the desktop.
 162      * @return the set of tools currently on the desktop
 163      */
 164     public abstract Tool[] getTools();
 165 
 166     /**
 167      * Add a new tool to the desktop.
 168      * @param t the tool to be added
 169      * @see #removeTool
 170      */
 171     public abstract void addTool(Tool t);
 172 
 173     /**
 174      * Remove a tool from the desktop.
 175      * @param t the tool to be removed
 176      * @see #addTool
 177      */
 178     public abstract void removeTool(Tool t);
 179 
 180     /**
 181      * Get the currently selected tool on the desktop.
 182      * @return the currently selected tool on the desktop
 183      * @see #setSelectedTool
 184      */
 185     public abstract Tool getSelectedTool();
 186 
 187     /**
 188      * Set the currently selected tool on the desktop.
 189      * @param t the the tool to be selected on the desktop
 190      * @see #getSelectedTool
 191      */
 192     public abstract void setSelectedTool(Tool t);
 193 
 194     //--------------------------------------------------------------------------
 195 
 196     /**
 197      * Get an integer signifying the style of the current desktop.
 198      * @return an integer signifying the style of the current desktop
 199      * @see #TAB_STYLE
 200      * @see #MDI_STYLE
 201      * @see #SDI_STYLE
 202      * @see #setStyle
 203      */
 204     public abstract int getStyle();
 205 
 206     /**
 207      * Get the top level frames that make up this desktop. TAB and MDI style
 208      * desktops just have a single frame; An SDI style desktop may have more
 209      * than one frame.
 210      * @return the top level frames of this desktop
 211      */
 212     public abstract JFrame[] getFrames();
 213 
 214     /**
 215      * Get a parent component for a dialog to use.
 216      * @return a component which can be used as a parent, or null if none
 217      *         is available.
 218      */
 219     public Component getDialogParent() {
 220         JFrame[] frames = getFrames();
 221         if (frames == null || frames.length == 0)
 222             return null;
 223         return frames[0].getContentPane();
 224     }
 225 
 226     /**
 227      * Get the bounds for this desktop.
 228      * @return a rectangle describing the size and position of this desktop
 229      * on the screen.
 230      */
 231     public abstract Rectangle getBounds();
 232 
 233     /**
 234      * Get thedefault bounds for a JT Harness desktop.
 235      * @return a rectangle describing the size and position on the screen
 236      * of the default desktop.
 237      */
 238     public static Rectangle getDefaultBounds() {
 239         GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
 240         Rectangle mwb = ge.getMaximumWindowBounds();
 241         int w = Math.min(mwb.width, Math.max(640, mwb.width * 3 / 4));
 242         int h = Math.min(mwb.height, Math.max(480, mwb.height * 3 / 4));
 243 
 244         int x = ge.getCenterPoint().x - w/2;
 245         int y = ge.getCenterPoint().y - h/2;
 246 
 247         return new Rectangle(x, y, w, h);
 248     }
 249 
 250     /**
 251      * Create a new top level frame for the desktop. The frame will have the
 252      * standard JT Harness menu bar.
 253      * @param winMenuListener a listener to be invoked when the user clicks on
 254      * the standard Windows menu
 255      * @param uiKey key to use for retrieving accessibility info for the frame
 256      * @return a new top level frame for the desktop
 257      */
 258     protected JFrame createFrame(MenuListener winMenuListener, String uiKey) {
 259         return createFrame(winMenuListener, null, uiKey);
 260     }
 261 
 262     /**
 263      * Create a new top level frame for the desktop. The frame will have the
 264      * standard JT Harness menu bar.
 265      * @param winMenuListener a listener to be invoked when the user clicks on
 266      * the standard Windows menu
 267      * @param fileCloseAction an action to use to create the File>Close menu item
 268      * to close the frame
 269      * @param uiKey key to use for retrieving accessibility info for the frame
 270      * @return a new top level frame for the desktop
 271      */
 272     protected JFrame createFrame(MenuListener winMenuListener, Action fileCloseAction,
 273                                  String uiKey) {
 274         JFrame frame = new JFrame();
 275         frame.setName(uiKey + ":" + (frameIndex++));
 276         frame.setTitle(uif.getI18NString("dt.title.txt"));
 277         frame.setIconImage(uif.createImage("images/jticon.gif"));
 278         uif.setAccessibleInfo(frame, uiKey);
 279 
 280         JRootPane root = frame.getRootPane();
 281         root.setName("root");
 282         uif.setAccessibleInfo(root, uiKey); // same as for frame, deliberate
 283 
 284         //System.err.println("DT: createFrame");
 285 
 286         JMenuBar mb = uif.createMenuBar("dt.menuBar");
 287         JMenu fileMenu = uif.createMenu("dt.file");
 288         // fileMenu is populated dynamically by FileMenuListener
 289         fileMenu.addMenuListener(new FileMenuListener(fileCloseAction));
 290         mb.add(fileMenu);
 291 
 292         JMenu toolMenu = uif.createMenu("dt.tasks");
 293         // could dynamically create this list when menu invoked
 294         ToolManager[] mgrs = desktop.getToolManagers();
 295         for (int i = 0; i < mgrs.length; i++) {
 296             //Action[] actions = mgrs[i].getTaskMenuActions();
 297             Action[] actions = mgrs[i].getWindowOpenMenuActions();  // method name is out of date
 298             if (actions != null) {
 299                 for (int j = 0; j < actions.length; j++)
 300                     toolMenu.add(actions[j]);
 301             }
 302         }
 303         mb.add(toolMenu);
 304 
 305         JMenu winMenu = uif.createMenu("dt.windows");
 306         // winMenu is populated dynamically by WinMenuListener
 307         winMenu.addMenuListener(winMenuListener);
 308         mb.add(winMenu);
 309 
 310         mb.add(uif.createHorizontalGlue("dt.pad"));
 311 
 312         JMenu helpMenu = new HelpMenu(frame, desktop, uif);
 313         mb.add(helpMenu);
 314 
 315         frame.setJMenuBar(mb);
 316 
 317         final HelpBroker helpBroker = desktop.getHelpBroker();
 318         JRootPane rootPane = frame.getRootPane();
 319         KeyStroke keystroke = KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0, false);
 320         rootPane.registerKeyboardAction(new ActionListener(){
 321                                             @Override
 322                                             public void actionPerformed(ActionEvent e) {
 323                                                 helpBroker.displayCurrentID(ContextHelpManager.getHelpIDString(getSelectedTool()));
 324                                             }
 325                                         }, keystroke,
 326                 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 327 
 328         Desktop.addPreferredSizeDebugListener(frame);
 329 
 330         if (focusMonitor != null)
 331             focusMonitor.monitor(frame);
 332 
 333         frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
 334         frame.addWindowListener(new WindowAdapter() {
 335             public void windowClosed(WindowEvent e) {
 336                 // WARNING: this event may be called more than once
 337                 // so only do post-processing the first time it is called
 338                 // for this window
 339                 final JFrame frame = (JFrame) (e.getSource());
 340                 //System.err.println("DT: closed " + frame.getTitle());
 341                 synchronized (allFrames) {
 342                     if (allFrames.remove(frame) && allFrames.isEmpty()) {
 343                         // defer until outstanding events are processed
 344                         EventQueue.invokeLater(new Runnable() {
 345                             public void run() {
 346                                 ExitCount.dec();
 347                             }
 348                         });
 349                     }
 350                 }
 351             }
 352         });
 353 
 354         synchronized (allFrames) {
 355             allFrames.add(frame);
 356             if (allFrames.size() == 1)
 357                 ExitCount.inc();
 358         }
 359 
 360         //System.err.println("DT: createFrame done");
 361 
 362         return frame;
 363     }
 364 
 365     /**
 366      * Add tool menu items from the given tool into the given frame's
 367      * tool menu.
 368      * @param frame the frame to modify
 369      * @param tool the tool to get the menu items from
 370      */
 371     protected void addToolMenuItemsToFrameMenuBar(JFrame frame, Tool tool) {
 372         JMenuBar frameMenuBar = frame.getJMenuBar();
 373         Tool curr = (Tool)(frameMenuBar.getClientProperty(getClass()));
 374         if (tool == curr)
 375             return;
 376         else if (curr != null)
 377             removeToolMenuItemsFromFrameMenuBar(frame, curr);
 378 
 379         JMenuBar toolMenuBar = tool.getMenuBar();
 380         if (toolMenuBar == null)
 381             return;
 382 
 383         for (int i = 0; i < toolMenuBar.getMenuCount(); i++) {
 384             JMenu toolMenu = toolMenuBar.getMenu(i);
 385             if (toolMenu == null)
 386                 // null if i'th item not a menu, e.g. glue
 387                 continue;
 388 
 389             int toolMenuSize = toolMenu.getMenuComponentCount();
 390             JMenu frameMenu = findMenu(frameMenuBar, toolMenu.getText());
 391             if (frameMenu == null) {
 392                 frameMenu = new JMenu(toolMenu.getText());
 393                 frameMenu.setName(toolMenu.getName());
 394                 frameMenu.setMnemonic(toolMenu.getMnemonic());
 395                 AccessibleContext bc = frameMenu.getAccessibleContext();
 396                 AccessibleContext tc = toolMenu.getAccessibleContext();
 397                 bc.setAccessibleName(tc.getAccessibleName());
 398                 bc.setAccessibleDescription(tc.getAccessibleDescription());
 399                 copyMenuListeners(toolMenu, frameMenu);
 400                 for (int j = 0; j < toolMenuSize; j++) {
 401                     frameMenu.add(toolMenu.getMenuComponent(0));
 402                 }
 403                 frameMenuBar.add(frameMenu, frameMenuBar.getMenuCount() - MENU_INSERT_POINT);
 404             }
 405             else {
 406                 for (int j = 0; j < toolMenuSize; j++) {
 407                     frameMenu.add(toolMenu.getMenuComponent(0), j);
 408                 }
 409                 frameMenu.insertSeparator(toolMenuSize);
 410             }
 411             frameMenu.putClientProperty(getClass(), new Integer(toolMenuSize));
 412         }   // for
 413         frameMenuBar.putClientProperty(getClass(), tool);
 414     }
 415 
 416     private void removeToolMenuItemsFromFrameMenuBar(JFrame frame) {
 417         JMenuBar frameMenuBar = frame.getJMenuBar();
 418         Tool tool = (Tool)(frameMenuBar.getClientProperty(getClass()));
 419         if (tool == null)
 420             return;
 421         removeToolMenuItemsFromFrameMenuBar(frame, tool);
 422     }
 423 
 424     /**
 425      * Remove the tool menu items for the given tool from the given frame's
 426      * tool menu.
 427      * @param frame the frame with the menu bar to which the tool's menus have
 428      * been added
 429      * @param tool the tool to which to return the menu items
 430      */
 431     protected void removeToolMenuItemsFromFrameMenuBar(JFrame frame, Tool tool) {
 432         JMenuBar frameMenuBar = frame.getJMenuBar();
 433         JMenuBar toolMenuBar = tool.getMenuBar();
 434         for (int i = 0; i < toolMenuBar.getMenuCount(); i++) {
 435             JMenu toolMenu = toolMenuBar.getMenu(i);
 436             if (toolMenu == null)
 437                 // null if i'th item not a menu, e.g. glue
 438                 continue;
 439 
 440             JMenu frameMenu = findMenu(frameMenuBar, toolMenu.getText());
 441             int toolMenuSize = ((Integer)(frameMenu.getClientProperty(getClass()))).intValue();
 442             for (int j = 0; j < toolMenuSize; j++) {
 443                 toolMenu.add(frameMenu.getMenuComponent(0));
 444             }
 445             if (frameMenu.getItemCount() == 0)
 446                 frameMenuBar.remove(frameMenu);
 447             else {
 448                 frameMenu.remove(0); // separator
 449                 frameMenu.putClientProperty(getClass(), null);
 450             }
 451         }
 452 
 453         frameMenuBar.putClientProperty(getClass(), null);
 454     }
 455 
 456     protected JMenu getWindowOpenMenu() {
 457         JMenu menu = uif.createMenu("dt.windows.open");
 458         // could dynamically create this list when menu invoked
 459         ToolManager[] mgrs = desktop.getToolManagers();
 460         for (int i = 0; i < mgrs.length; i++) {
 461             Action[] actions = mgrs[i].getWindowOpenMenuActions();
 462             if (actions != null) {
 463                 for (int j = 0; j < actions.length; j++)
 464                     menu.add(actions[j]);
 465             }
 466         }
 467         return menu;
 468     }
 469 
 470     private void copyMenuListeners(JMenu src, JMenu dst) {
 471         MenuListener[] ll = src.getListeners(MenuListener.class);
 472         for (int i = 0; i < ll.length; i++)
 473             dst.addMenuListener(ll[i]);
 474     }
 475 
 476     private JMenu findMenu(JMenuBar mb, String text) {
 477         for (int i = 0; i < mb.getMenuCount(); i++) {
 478             JMenu m = mb.getMenu(i);
 479             if (m != null && m.getText().equals(text))
 480                 return m;
 481         }
 482         return null;
 483     }
 484 
 485     private static final int MENU_INSERT_POINT = 4; // before Windows, glue and Help
 486 
 487     /**
 488      * Create a dialog.
 489      * @param tool the parent tool for the dialog
 490      * @param uiKey a string which is to be used as the base name for any
 491      * resources that may be required
 492      * @param title the title for the dialog
 493      * @param menuBar the menu bar for the dialog
 494      * @param body the body component for the dialog
 495      * @param bounds the size and position for the dialog
 496      * @return a JDialog or JInternalDialog built from the supplied values.
 497      */
 498     public abstract Container createDialog(Tool tool, String uiKey, String title,
 499                                            JMenuBar menuBar, Container body,
 500                                            Rectangle bounds, int type);
 501 
 502     /**
 503      * Check if the tool's parent Window is the owner of a dialog.
 504      * This may become false if the desktop style is changed after the dialog
 505      * was created.
 506      * @param tool the tool from which to determine the parent Window
 507      * @param dialog the dialog to be checked
 508      * @return true if the tool's parent Window is the owner of the dialog, and
 509      * false otherwise.
 510      */
 511     public abstract boolean isToolOwnerForDialog(Tool tool, Container dialog);
 512 
 513     /**
 514      * Handle the File>Preferences menu: show a Preferences window.
 515      */
 516     private void doPrefs(JFrame parent) {
 517         desktop.showPreferences(parent);
 518     }
 519 
 520     /**
 521      * Save the state of the desktop to a map object.
 522      * @param m The map object to which to write the state of the desktop
 523      * @see #restoreDesktop
 524      */
 525     protected abstract void saveDesktop(Map<String, String> m);
 526 
 527     /**
 528      * Save the state of the current tools on the desktop to a map object.
 529      * @param m The map object to which to write the state of the desktop
 530      * @see #restoreDesktop
 531      */
 532     protected void saveTools(Map<String, String> m) {
 533         Tool[] tools = getTools();
 534         m.put("tool.count", String.valueOf(tools.length));
 535         for (int i = 0; i < tools.length; i++)
 536             saveTool(new PrefixMap<>(m, "tool." + String.valueOf(i)), tools[i]);
 537     }
 538 
 539     /**
 540      * Save information about a tool on the desktop.
 541      * @param m A map object in which to save the information
 542      * @param t the tool to be saved
 543      * @see #restoreTool
 544      */
 545     protected void saveTool(Map<String, String> m, Tool t) {
 546         m.put("mgr", t.getManager().getClass().getName());
 547         m.put("class", t.getClass().getName());
 548         m.put("selected", String.valueOf(t == getSelectedTool()));
 549         t.save(m);
 550     }
 551 
 552     /**
 553      * Restore the state of the desktop from a map object.
 554      * @param m The map object from which to restore the state of the desktop
 555      * @see #saveDesktop
 556      */
 557     protected abstract void restoreDesktop(Map<String, String> m);
 558 
 559     /**
 560      * Restore the state of the saved tools from a map object.
 561      * @param m The map object from which to restore the state of the saved tools
 562      * @see #saveTools
 563      */
 564     protected void restoreTools(Map<String, String> m) {
 565         try {
 566             String c = m.get("tool.count");
 567             if (c != null) {
 568                 int count = Integer.parseInt(c);
 569                 for (int i = 0; i < count; i++) {
 570                     try {
 571                         String prefix = "tool." + i;
 572                         Map<String, String> toolMap = new PrefixMap<>(m, prefix);
 573                         restoreTool(toolMap, prefix);
 574                     }
 575                     catch (ToolManager.Fault e) {
 576                         uif.showError("dv.restore.cantRestoreTool",
 577                                       new Object[] { new Integer(i), e.getMessage() });
 578                     }
 579                     catch (Fault e) {
 580                         uif.showError("dv.restore.cantRestoreTool",
 581                                       new Object[] { new Integer(i), e.getMessage() });
 582                     }
 583                     catch (Throwable e) {
 584                         uif.showError("dv.restore.cantRestoreTool",
 585                                       new Object[] { new Integer(i), e.toString() });
 586                         I18NResourceBundle i18n = uif.getI18NResourceBundle();
 587                         desktop.log(i18n, "dv.restore.cantRestoreTool",
 588                                     new Object[] { e, new Integer(i) });
 589                     }
 590                 }
 591             }
 592         }
 593         catch (NumberFormatException ignore) {
 594             // ignore, for now
 595         }
 596 
 597     }
 598 
 599     /**
 600      * Restore a tool on the desktop from information in a map object.
 601      * @param m The map containing the information about a tool
 602      * @return the tool that was restored from the specified information in the map
 603      * @see #saveTool
 604      * @throws ClassNotFoundException if the class for the tool cannot be found
 605      * @throws NoSuchMethodException if the tool does not have the appropriate restore method
 606      * @throws IllegalAccessException if the tool's restore method does not have public access
 607      * @throws InvocationTargetException if the tool's restore method threw an exception
 608      */
 609     protected Tool restoreTool(Map<String, String> m, String name) throws Fault, ToolManager.Fault
 610     {
 611         String mgrClassName = m.get("mgr");
 612         if (mgrClassName == null) {
 613             // backwards compatibility with 3.1
 614             String toolClassName = m.get("class");
 615             if (toolClassName != null && toolClassName.endsWith("Tool")) {
 616                 String n = toolClassName.substring(0, toolClassName.length()) + "Manager";
 617                 try {
 618                     if (Class.forName(n) != null)
 619                         mgrClassName = n;
 620                 }
 621                 catch (Throwable e) {
 622                     // ignore
 623                 }
 624             }
 625 
 626             if (mgrClassName == null)
 627                 throw new Fault(i18n, "dv.restore.noMgrClass", name);
 628         }
 629 
 630         ToolManager mgr = desktop.getToolManager(mgrClassName);
 631         if (mgr == null)
 632             throw new Fault(i18n, "dv.restore.noMgr",
 633                             new Object[] { name, mgrClassName } );
 634 
 635         Tool t = mgr.restoreTool(m);
 636 
 637         addTool(t);
 638         boolean selected = "true".equals(m.get("selected"));
 639         if (selected)
 640             setSelectedTool(t);
 641 
 642         return t;
 643     }
 644 
 645     /**
 646      * Save the screen size and position for a component in a map object.
 647      * @param c the component whose size and position are to be recorded in the map.
 648      * @param m the map in which the information is to be recorded
 649      * @see #restoreBounds
 650      */
 651     protected static void saveBounds(Component c, Map<String, String> m) {
 652         Rectangle r = c.getBounds();
 653         m.put("x", String.valueOf(r.x));
 654         m.put("y", String.valueOf(r.y));
 655         m.put("w", String.valueOf(r.width));
 656         m.put("h", String.valueOf(r.height));
 657     }
 658 
 659 
 660     /**
 661      * Restore the screen size and position for a component from information
 662      * in a map object.
 663      * @param c the component whose size and position are to be restored.
 664      * @param m the map in which the information is to be recorded
 665      * @see #saveBounds
 666      */
 667     protected static void restoreBounds(Component c, Map<String, String> m) {
 668         try {
 669             String xs = (m.get("x"));
 670             String ys = (m.get("y"));
 671             String ws = (m.get("w"));
 672             String hs = (m.get("h"));
 673             if (xs != null && ys != null && ws != null && hs != null) {
 674                 Rectangle restored = new Rectangle(Integer.parseInt(xs),
 675                         Integer.parseInt(ys),
 676                         Integer.parseInt(ws),
 677                         Integer.parseInt(hs));
 678 
 679                 restored = getScreenBounds().intersection(restored);
 680                 if (!restored.isEmpty()) {
 681                     c.setBounds(restored);
 682                 }
 683 
 684             }
 685         }
 686         catch (NumberFormatException e) {
 687             // ignore
 688         }
 689     }
 690 
 691     public static Rectangle getScreenBounds() {
 692         // Consider multi-display environment
 693         Rectangle result = new Rectangle();
 694         GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
 695         GraphicsDevice[] gs = ge.getScreenDevices();
 696         for (int j = 0; j < gs.length; j++) {
 697             GraphicsDevice gd = gs[j];
 698             GraphicsConfiguration[] gc = gd.getConfigurations();
 699             for (int i = 0; i < gc.length; i++) {
 700                 result = result.union(gc[i].getBounds());
 701             }
 702         }
 703 
 704         return result;
 705     }
 706 
 707 
 708     // keep a private list of all the outstanding frames, so that
 709     // when the collection becomes empty, we can lower the ExitCount.
 710     private static Collection<JFrame> allFrames = new Vector<>();
 711 
 712     // counter for unique name generation
 713     private static int frameIndex;
 714 
 715     private Desktop desktop;
 716 
 717     /**
 718      * The UI factory used to create GUI components.
 719      */
 720     protected final UIFactory uif;
 721 
 722     private static FocusMonitor focusMonitor;
 723 
 724     static {
 725         String opts = System.getProperty("javatest.focus.monitor");
 726         if (opts != null) {
 727             focusMonitor = FocusMonitor.access();
 728             if (!opts.equals("true")) // support old value for back compat
 729                 focusMonitor.setOptions(StringArray.split(opts));
 730             focusMonitor.setActivateKey("alt 2");
 731             focusMonitor.setReportKey("shift alt 2");
 732             focusMonitor.setReportFile(System.getProperty("javatest.focus.monitor.log"));
 733         }
 734     };
 735 
 736             static final String CLOSE = "close"; // visible for file close listeners
 737     private static final String EXIT = "exit";
 738     private static final String PREFS = "prefs";
 739     private static final String HISTORY = "history";
 740     private static final String SEPARATOR = null;
 741 
 742     private static final I18NResourceBundle i18n =
 743         I18NResourceBundle.getBundleForClass(DeskView.class);
 744 
 745 
 746     //-------------------------------------------------------------------------
 747 
 748     private class FileMenuListener implements MenuListener, ActionListener {
 749         FileMenuListener(Action closeAction) {
 750 
 751             prefs = uif.createMenuItem("dt.file", PREFS, this);
 752             if (closeAction != null)
 753                 close = uif.createMenuItem(closeAction);
 754             exit = uif.createMenuItem("dt.file", EXIT, this);
 755             // additional entries are created dynamically
 756         }
 757 
 758         public void menuSelected(MenuEvent e) {
 759             //System.err.println("DT:FileMenu: selected " + e);
 760             JMenu m = (JMenu) (e.getSource());
 761             m.removeAll();
 762 
 763             // this code is not ideal, and essentially works (for now) because
 764             // only one tool manager provides file menu actions: ie. ExecManager.
 765             // If that gets changed, we'll want to consider sorting/grouping
 766             // the actions somehow.
 767             ToolManager[] mgrs = desktop.getToolManagers();
 768             for (int i = 0; i < mgrs.length; i++) {
 769                 Action[] fma = mgrs[i].getFileMenuActions();
 770                 if (fma != null) {
 771                     for (int j = 0; j < fma.length; j++)
 772                         m.add(new JMenuItem(fma[j]));
 773                 }
 774 
 775                 JMenuItem[] jmi = mgrs[i].getFileMenuPrimaries();
 776                 if (jmi != null) {
 777                     for (int j = 0; j < jmi.length; j++)
 778                         m.add(jmi[j]);
 779                 }
 780 
 781             }   // for
 782 
 783             // add secondary items before prefs, close, exit
 784             for (int i = 0; i < mgrs.length; i++) {
 785                 JMenuItem[] jmi = mgrs[i].getFileMenuSecondaries();
 786                 if (jmi != null) {
 787                     for (int j = 0; j < jmi.length; j++)
 788                         m.add(jmi[j]);
 789                     m.addSeparator();
 790                 }
 791 
 792             }   // for
 793 
 794             List<Desktop.FileHistoryEntry> fileHistory = desktop.getFileHistory();
 795 //          if (!fileHistory.isEmpty()) {
 796         JMenu hm = uif.createMenu("dt.file.recentwd");
 797         if (!fileHistory.isEmpty()) {
 798                 int n = 0;
 799 
 800                 for (Iterator<Desktop.FileHistoryEntry> i = fileHistory.iterator(); i.hasNext(); ) {
 801                     Desktop.FileHistoryEntry h = (i.next());
 802                     if (!h.file.exists())
 803                         continue;
 804                     String s = uif.getI18NString("dt.file.historyX.mit",
 805                                              new Object[] {new Integer(n), h.file.getPath()});
 806                     JMenuItem mi = new JMenuItem(s);
 807                     mi.setActionCommand(HISTORY);
 808                     mi.addActionListener(this);
 809                     mi.putClientProperty(this, h);
 810                     if (n < 10)
 811                         mi.setMnemonic(Character.forDigit(n, 10));
 812                     n++;
 813                     hm.add(mi);
 814                 }
 815         } else {
 816                 JMenuItem noEntries = new JMenuItem(i18n.getString("fh.empty"));
 817                 noEntries.setEnabled(false);
 818                 hm.add(noEntries);
 819 
 820         }
 821         m.add(hm);
 822                 m.addSeparator();
 823 
 824             m.add(prefs);
 825             m.addSeparator();
 826             if (close != null)
 827                 m.add(close);
 828             m.add(exit);
 829         }
 830 
 831         public void menuDeselected(MenuEvent e) {
 832             JMenu m = (JMenu) (e.getSource());
 833             m.removeAll();
 834         }
 835 
 836         public void menuCanceled(MenuEvent e) {
 837             JMenu m = (JMenu) (e.getSource());
 838             m.removeAll();
 839         }
 840 
 841         public void actionPerformed(ActionEvent e) {
 842             //System.err.println("DT:FileMenu: action " + e);
 843             Component src = (Component) (e.getSource());
 844             JFrame parent = (JFrame) (SwingUtilities.getAncestorOfClass(JFrame.class, src));
 845             if (parent == null) // only happens during testing when invoking menuitem directly,
 846                 parent = getFrames()[0];
 847 
 848             String cmd = e.getActionCommand();
 849             if (cmd.equals(PREFS)) {
 850                 doPrefs(parent);
 851             }
 852             else if (cmd.equals(HISTORY)) {
 853                 JMenuItem mi = (JMenuItem) (e.getSource());
 854                 Desktop.FileHistoryEntry h = (Desktop.FileHistoryEntry) (mi.getClientProperty(this));
 855                 try {
 856                     h.fileOpener.open(h.file);
 857                 }
 858                 catch (FileNotFoundException ex) {
 859                     uif.showError("dt.file.cannotFind", h.file);
 860                 }
 861                 catch (FileOpener.Fault ex) {
 862                     uif.showError("dt.file.cannotOpen",
 863                                   new Object[] { h.file, ex.getMessage() });
 864                 }
 865             }
 866             else if (cmd.equals(EXIT)) {
 867                 desktop.checkToolsAndExitIfOK(parent);
 868             }
 869         }
 870 
 871         private JMenu fileOpenMenu;
 872         private JMenuItem prefs;
 873         private JMenuItem close;
 874         private JMenuItem exit;
 875     }
 876 
 877 
 878 }