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.Dimension; 32 import java.awt.FocusTraversalPolicy; 33 import java.awt.KeyboardFocusManager; 34 import java.awt.Rectangle; 35 import java.awt.Window; 36 import java.awt.event.ActionEvent; 37 import java.awt.event.ActionListener; 38 import java.awt.event.WindowAdapter; 39 import java.awt.event.WindowEvent; 40 import java.awt.Dialog.ModalityType; 41 import java.util.Arrays; 42 import java.util.Comparator; 43 import java.util.HashSet; 44 import java.util.Map; 45 import java.util.Set; 46 47 import javax.swing.Action; 48 import javax.swing.JDialog; 49 import javax.swing.JFrame; 50 import javax.swing.JMenu; 51 import javax.swing.JMenuBar; 52 import javax.swing.JMenuItem; 53 import javax.swing.JTabbedPane; 54 import javax.swing.SwingConstants; 55 import javax.swing.event.AncestorEvent; 56 import javax.swing.event.AncestorListener; 57 import javax.swing.event.ChangeEvent; 58 import javax.swing.event.ChangeListener; 59 import javax.swing.event.MenuEvent; 60 import javax.swing.event.MenuListener; 61 62 import com.sun.javatest.util.PrefixMap; 63 import com.sun.javatest.tool.jthelp.ContextHelpManager; 64 65 /** 66 * A container that presents the current desktop tools in a tabbed pane. 67 * The main complexity is that when a tool is made current, its menus 68 * are merged onto the main menu bar, and removed when the tool is no 69 * longer selected. 70 */ 71 class TabDeskView extends DeskView { 72 73 TabDeskView(Desktop desktop) { 74 this(desktop, getDefaultBounds()); 75 } 76 77 TabDeskView(DeskView other) { 78 this(other.getDesktop(), other.getBounds()); 79 //System.err.println("Tab: create from " + other); 80 //System.err.println("Tab: create " + other.getTools().length + " tools"); 81 82 Tool[] tools = other.getTools(); 83 84 // perhaps would be nice to have getTools(Comparator) and have it return a sorted 85 // array of tools 86 Arrays.sort(tools, new Comparator() { 87 public int compare(Object o1, Object o2) { 88 Long l1 = new Long(((Tool)o1).getCreationTime()); 89 Long l2 = new Long(((Tool)o2).getCreationTime()); 90 return (l1.compareTo(l2)); 91 } 92 }); 93 94 for (int i = 0; i < tools.length; i++) 95 addTool(tools[i]); 96 97 setVisible(other.isVisible()); 98 } 99 100 private TabDeskView(Desktop desktop, Rectangle bounds) { 101 super(desktop); 102 initMainFrame(bounds); 103 uif.setDialogParent(mainFrame); 104 JDialog.setDefaultLookAndFeelDecorated(false); 105 } 106 107 public void dispose() { 108 mainFrame.setVisible(false); 109 mainFrame.dispose(); 110 super.dispose(); 111 } 112 113 public boolean isVisible() { 114 return mainFrame.isVisible(); 115 } 116 117 public void setVisible(boolean v) { 118 //System.err.println("Tab: setVisible: " + v); 119 if (v == mainFrame.isVisible()) 120 return; 121 122 mainFrame.setVisible(v); 123 124 if (v) { 125 Window[] ww = mainFrame.getOwnedWindows(); 126 if (ww != null) { 127 for (int i = 0; i < ww.length; i++) 128 ww[i].toFront(); 129 } 130 } 131 } 132 133 public void addTool(Tool t) { 134 DeskView view = t.getDeskView(); 135 if (view == this) 136 return; 137 138 // save info about dialogs before we remove tool from other view 139 ToolDialog[] tds = t.getToolDialogs(); 140 boolean[] vis = new boolean[tds.length]; 141 for (int i = 0; i < tds.length; i++) 142 vis[i] = tds[i].isVisible(); 143 144 // remove tool from other view (if any) 145 if (view != null) 146 view.removeTool(t); 147 148 //System.err.println("Tab: add " + t); 149 String tabTitle = getUniqueTabTitle(t.getShortTitle(), null); 150 String tabToolTip = t.getTitle(); 151 contents.addTab(tabTitle, null, t, tabToolTip); 152 t.addObserver(listener); 153 closeAction.setEnabled(true); 154 155 t.setDeskView(this); 156 157 // update tool dialogs 158 for (int i = 0; i < tds.length; i++) 159 tds[i].initDialog(this, vis[i]); 160 } 161 162 public boolean isEmpty() { 163 return (contents.getComponentCount() == 0); 164 } 165 166 public Tool[] getTools() { 167 Tool[] tools = new Tool[contents.getComponentCount()]; 168 for (int i = 0; i < tools.length; i++) 169 tools[i] = (Tool) (contents.getComponentAt(i)); 170 return tools; 171 } 172 173 public void removeTool(Tool t) { 174 t.removeObserver(listener); 175 176 // remove the change listener temporarily because of the 177 // broken semantics 178 contents.removeChangeListener(listener); 179 180 // remove the tool 181 contents.remove(t); 182 t.setDeskView(null); 183 184 // update the selection as appropriate 185 if (t == selectedTool) 186 // set a different tool to be selected 187 setSelectedTool((Tool) contents.getSelectedComponent()); 188 else 189 // set the selection again, in case the index has changed 190 contents.setSelectedComponent(selectedTool); 191 192 // ensure there is a valid keyboard focus 193 KeyboardFocusManager fm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); 194 Component fo = fm.getPermanentFocusOwner(); 195 if (fo == null || !fo.isShowing()) { 196 Container target = contents.getTabCount() > 0 ? contents : mainFrame; 197 Container fcr = (target.isFocusCycleRoot() ? target : target.getFocusCycleRootAncestor()); 198 FocusTraversalPolicy ftp = fcr.getFocusTraversalPolicy(); 199 Component c = (target.isFocusable() ? target : ftp.getComponentAfter(fcr, target)); 200 c.requestFocusInWindow(); 201 } 202 203 // restore the change listener now that the removal has been done 204 contents.addChangeListener(listener); 205 206 // update actions 207 closeAction.setEnabled(contents.getTabCount() > 0); 208 } 209 210 public Tool getSelectedTool() { 211 return selectedTool; 212 } 213 214 public void setSelectedTool(Tool t) { 215 if (t == selectedTool) 216 // already selected 217 return; 218 219 // hands off the old selected tool (if any) 220 if (selectedTool != null) { 221 //OLD removeToolMenuItemsFromBasicMenuBar(selectedTool); 222 removeToolMenuItemsFromFrameMenuBar(mainFrame, selectedTool); 223 selectedTool.removeObserver(listener); 224 } 225 226 selectedTool = t; 227 228 // hands on the new selected tool (if any) 229 if (selectedTool == null) { 230 mainFrame.setTitle(uif.getI18NString("dt.title.txt")); 231 ContextHelpManager.setHelpIDString(contents, null); 232 } 233 else { 234 //OLD addToolMenuItemsToBasicMenuBar(selectedTool); 235 addToolMenuItemsToFrameMenuBar(mainFrame, selectedTool); 236 selectedTool.addObserver(listener); 237 mainFrame.setTitle(uif.getI18NString("dt.title.tool.txt", selectedTool.getTitle())); 238 ContextHelpManager.setHelpIDString(contents, ContextHelpManager.getHelpIDString(selectedTool)); 239 contents.setSelectedComponent(selectedTool); 240 } 241 242 } 243 244 public int getStyle() { 245 return Desktop.TAB_STYLE; 246 } 247 248 public JFrame[] getFrames() { 249 return new JFrame[] { mainFrame }; 250 } 251 252 public Rectangle getBounds() { 253 return mainFrame.getBounds(); 254 } 255 256 public boolean isToolOwnerForDialog(Tool tool, Container dialog) { 257 for (ToolDialog td: tool.getToolDialogs()) { 258 if (td.getDialogParent() == dialog) 259 return true; 260 } 261 return (dialog != null 262 && (dialog.getParent() == mainFrame)); 263 } 264 265 public Window createDialog(Tool tool, String uiKey, String title, 266 JMenuBar menuBar, Container body, 267 Rectangle bounds, int type) { 268 UIFactory uif = tool.uif; 269 if ((type & ToolDialog.FRAME) != 0) { 270 JFrame d = uif.createFrame(uiKey, title, body); 271 if (menuBar != null) 272 d.setJMenuBar(menuBar); 273 274 setBounds(d, bounds); 275 276 return d; 277 } else /*if ((type & ToolDialog.DIALOG) != 0)*/ { 278 JFrame owner = mainFrame; 279 if ((type & ToolDialog.FREE) != 0) { 280 owner = null; 281 } 282 283 JDialog d = uif.createDialog(uiKey, owner, title, body); 284 285 if ((type & ToolDialog.MODAL) != 0) { 286 if ((type & ToolDialog.MODAL_TOOLKIT) == ToolDialog.MODAL_TOOLKIT) { 287 d.setModalityType(ModalityType.TOOLKIT_MODAL); 288 } else if ((type & ToolDialog.MODAL_DOCUMENT) == ToolDialog.MODAL_DOCUMENT) { 289 d.setModalityType(ModalityType.DOCUMENT_MODAL); 290 } else if ((type & ToolDialog.MODAL_APPLICATION) == ToolDialog.MODAL_APPLICATION) { 291 d.setModalityType(ModalityType.APPLICATION_MODAL); 292 } else { 293 d.setModal(true); 294 } 295 } 296 if (menuBar != null) 297 d.setJMenuBar(menuBar); 298 299 setBounds(d, bounds); 300 301 return d; 302 } 303 } 304 305 private void setBounds(Window d, Rectangle bounds) { 306 if (bounds == null) { 307 d.pack(); 308 // for some reason the first call of pack seems to yield small results 309 // so we need to pack it again to get the real results. Additional calls 310 // seem to have no effect, so after 2 calls we seem to have stable results. 311 d.pack(); 312 d.setLocationRelativeTo(mainFrame); 313 } else { 314 d.setBounds(bounds); 315 } 316 } 317 318 public Container createDialog(Tool tool, String uiKey, String title, 319 JMenuBar menuBar, Container body, 320 Rectangle bounds) { 321 UIFactory uif = tool.uif; 322 JDialog d = uif.createDialog(uiKey, mainFrame, title, body); 323 if (menuBar != null) 324 d.setJMenuBar(menuBar); 325 326 setBounds(d, bounds); 327 328 return d; 329 } 330 331 protected void saveDesktop(Map<String, String> m) { 332 saveBounds(mainFrame, new PrefixMap<>(m, "dt")); 333 saveTools(m); 334 int sel = contents.getSelectedIndex(); 335 if (sel >= 0) 336 m.put("dt.selected", String.valueOf(sel)); 337 } 338 339 protected void restoreDesktop(Map<String, String> m) { 340 restoreBounds(mainFrame, new PrefixMap<>(m, "dt")); 341 if (getDesktop().getRestoreOnStart()) { 342 restoreTools(m); 343 344 try { 345 String s = m.get("dt.selected"); 346 if (s != null) { 347 int sel = Integer.parseInt(s); 348 if (0 <= sel && sel < contents.getTabCount()) { 349 contents.setSelectedIndex(sel); 350 } 351 } 352 } catch (NumberFormatException e) { 353 // ignore 354 } 355 } 356 } 357 358 // internal 359 360 private void initMainFrame(Rectangle bounds) { 361 //System.err.println("Tab: create"); 362 mainFrame = createFrame(listener, closeAction, "tdi.main"); 363 //OLD basicMenuBar = mainFrame.getJMenuBar(); 364 365 contents = uif.createTabbedPane("tdi.desk"); 366 contents.setOpaque(true); 367 contents.setPreferredSize(new Dimension(bounds.width, bounds.height)); 368 369 // this could be a user preference (deck tab placement) 370 contents.setTabPlacement(SwingConstants.BOTTOM); 371 contents.addChangeListener(listener); 372 contents.addAncestorListener(listener); 373 mainFrame.setContentPane(contents); 374 mainFrame.setBounds(bounds); 375 376 mainFrame.addWindowListener(new WindowAdapter() { 377 public void windowClosing(WindowEvent e) { 378 getDesktop().checkToolsAndExitIfOK(mainFrame); 379 } 380 }); 381 } 382 383 private String getUniqueTabTitle(String base, Component ignoreable) { 384 Set<String> s = new HashSet<>(); 385 for (int i = 0; i < contents.getTabCount(); i++) { 386 if (contents.getComponentAt(i) != ignoreable) 387 s.add(contents.getTitleAt(i)); 388 } 389 390 if (s.contains(base)) { 391 for (int i = 0; i <= s.size(); i++) { 392 // checking s.size() + 1 cases: at least one must be free 393 String v = base + " [" + (i + 2) + "]"; 394 if (!s.contains(v)) 395 return v; 396 } 397 } 398 399 return base; 400 } 401 402 private JFrame mainFrame; 403 // OLD private JMenuBar basicMenuBar; 404 private JTabbedPane contents; 405 private Tool selectedTool; 406 407 private Listener listener = new Listener(); 408 409 private Action closeAction = new CloseAction(); 410 411 private class CloseAction extends ToolAction { 412 CloseAction() { 413 super(uif, "tdi.file.close"); 414 setEnabled(false); // enabled if/when there are tools 415 } 416 417 public void actionPerformed(ActionEvent e) { 418 Tool t = (Tool) (contents.getSelectedComponent()); 419 if (t != null) 420 // should never be null because action should be disabled if there 421 // are no tabs 422 if (getDesktop().isOKToClose(t, mainFrame)) { 423 removeTool(t); 424 t.dispose(); 425 } 426 } 427 }; 428 429 private class Listener 430 implements ActionListener, AncestorListener, 431 ChangeListener, MenuListener, 432 Tool.Observer 433 { 434 // --------- ActionListener --------- 435 436 public void actionPerformed(ActionEvent e) { 437 JMenuItem mi = (JMenuItem) (e.getSource()); 438 Object o = mi.getClientProperty(this); 439 if (o instanceof Window) 440 ((Window) o).toFront(); 441 else if (o instanceof Tool) 442 setSelectedTool((Tool) o); 443 } 444 445 // ---------- AncestorListener ---------- 446 447 public void ancestorAdded(AncestorEvent event) { 448 Tool t = (Tool)(contents.getSelectedComponent()); 449 if (t != null) { 450 // OLD addToolMenuItemsToBasicMenuBar(t); 451 addToolMenuItemsToFrameMenuBar(mainFrame, t); 452 t.addObserver(this); 453 mainFrame.setTitle(uif.getI18NString("dt.title.tool.txt", t.getTitle())); 454 } 455 } 456 457 public void ancestorMoved(AncestorEvent event) { } 458 459 public void ancestorRemoved(AncestorEvent event) { 460 // // update menubar 461 // removeToolMenuItemsFromBasicMenuBar(); 462 // stop observing current tool 463 Tool t = (Tool) (contents.getSelectedComponent()); 464 if (t != null) 465 t.removeObserver(this); 466 mainFrame.setTitle(uif.getI18NString("dt.title.txt")); 467 } 468 469 // ---------- ChangeListener ---------- 470 471 public void stateChanged(ChangeEvent e) { 472 setSelectedTool((Tool) (contents.getSelectedComponent())); 473 } 474 475 // --------- MenuListener --------- 476 477 // note this is for Windows menu only 478 public void menuSelected(MenuEvent e) { 479 Tool[] tools = getTools(); 480 481 JMenu m = (JMenu) (e.getSource()); 482 m.removeAll(); 483 484 /* 485 JMenu winOpenMenu = getWindowOpenMenu(); 486 if (winOpenMenu.getItemCount() > 0) { 487 m.add(getWindowOpenMenu()); 488 m.addSeparator(); 489 } 490 */ 491 492 // add entries for all current tools 493 if (tools.length == 0) { 494 JMenuItem mi = new JMenuItem(uif.getI18NString("dt.windows.noWindows.mit")); 495 mi.setEnabled(false); 496 m.add(mi); 497 } 498 else { 499 int n = 0; 500 501 // add entries for all current tools 502 for (int i = 0; i < tools.length; i++) { 503 Tool tool = tools[i]; 504 addMenuItem(m, n++, tool.getTitle(), tool); 505 } 506 507 // add entries for any dialogs 508 Window[] ownedWindows = mainFrame.getOwnedWindows(); 509 for (int i = 0; i < ownedWindows.length; i++) { 510 Window w = ownedWindows[i]; 511 if (w.isVisible()) { 512 if (w instanceof JDialog) 513 addMenuItem(m, n++, ((JDialog) w).getTitle(), w); 514 if (w instanceof JFrame) 515 addMenuItem(m, n++, ((JFrame) w).getTitle(), w); 516 } 517 } 518 } 519 } 520 521 private void addMenuItem(JMenu m, int n, String s, Object o) { 522 JMenuItem mi = new JMenuItem(uif.getI18NString("dt.windows.toolX.mit", 523 new Object[] { new Integer(n), s })); 524 if (n < 10) 525 mi.setMnemonic(Character.forDigit(n, 10)); 526 mi.addActionListener(this); 527 mi.putClientProperty(this, o); 528 m.add(mi); 529 } 530 531 public void menuDeselected(MenuEvent e) { 532 // it's not so much the menu items we want to get rid of, as the 533 // client properties on those items 534 JMenu m = (JMenu) (e.getSource()); 535 m.removeAll(); 536 } 537 538 public void menuCanceled(MenuEvent e) { 539 // it's not so much the menu items we want to get rid of, as the 540 // client properties on those items 541 JMenu m = (JMenu) (e.getSource()); 542 m.removeAll(); 543 } 544 545 // ---------- Tool.Observer ---------- 546 547 public void shortTitleChanged(Tool src, String newValue) { 548 for (int i = 0; i < contents.getTabCount(); i++) { 549 if (contents.getComponentAt(i) == src) { 550 String tabTitle = getUniqueTabTitle(newValue, src); 551 contents.setTitleAt(i, tabTitle); 552 break; 553 } 554 } 555 } 556 557 public void titleChanged(Tool src, String newValue) { 558 if (src == contents.getSelectedComponent()) 559 mainFrame.setTitle(uif.getI18NString("dt.title.tool.txt", newValue)); 560 561 for (int i = 0; i < contents.getTabCount(); i++) { 562 if (contents.getComponentAt(i) == src) { 563 contents.setToolTipTextAt(i, newValue); 564 break; 565 } 566 } 567 //System.err.println("Tool title changed: " + newValue); 568 } 569 570 public void toolDisposed(Tool src) { } 571 }; 572 }