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 }