1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2001, 2013, 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.EventQueue; 33 import java.awt.Rectangle; 34 import java.awt.Toolkit; 35 import java.awt.event.ActionEvent; 36 import java.awt.event.ActionListener; 37 import java.io.BufferedInputStream; 38 import java.io.BufferedOutputStream; 39 import java.io.File; 40 import java.io.FileInputStream; 41 import java.io.FileOutputStream; 42 import java.io.InputStream; 43 import java.io.IOException; 44 import java.io.OutputStream; 45 import java.io.StringWriter; 46 import java.lang.reflect.InvocationTargetException; 47 import java.lang.reflect.Method; 48 import java.net.URL; 49 import java.util.*; 50 import javax.swing.BorderFactory; 51 import javax.swing.Icon; 52 import javax.swing.JButton; 53 import javax.swing.JComponent; 54 import javax.swing.JDialog; 55 import javax.swing.JEditorPane; 56 import javax.swing.JFrame; 57 import javax.swing.JMenuBar; 58 import javax.swing.JOptionPane; 59 import javax.swing.JTextArea; 60 import javax.swing.KeyStroke; 61 import javax.swing.Timer; 62 import javax.swing.ToolTipManager; 63 import javax.swing.UIManager; 64 65 import com.sun.javatest.InterviewParameters; 66 import com.sun.javatest.JavaTestError; 67 import com.sun.javatest.TestSuite; 68 import com.sun.javatest.tool.jthelp.ContextHelpManager; 69 import com.sun.javatest.tool.jthelp.HelpBroker; 70 import com.sun.javatest.tool.jthelp.HelpSet; 71 import com.sun.javatest.tool.jthelp.JTHelpBroker; 72 import com.sun.javatest.util.*; 73 import com.sun.javatest.report.HTMLWriterEx; 74 import com.sun.javatest.util.Properties; 75 76 import java.awt.print.Printable; 77 import java.awt.print.PrinterJob; 78 import javax.print.Doc; 79 import javax.print.DocFlavor; 80 import javax.print.DocPrintJob; 81 import javax.print.PrintException; 82 import javax.print.PrintService; 83 import javax.print.PrintServiceLookup; 84 import javax.print.ServiceUI; 85 import javax.print.SimpleDoc; 86 import javax.print.attribute.HashPrintRequestAttributeSet; 87 import javax.print.attribute.PrintRequestAttributeSet; 88 import javax.swing.LookAndFeel; 89 90 /** 91 * Desktop is the host for a series of Tools, 92 * which may be displayed as in a number of styles, 93 * provided by a DeskView. 94 * <p>Much of the functionality of a desktop is provided by the current view, 95 * and because of that, many of the methods here simply call through to the 96 * underlying current view object. 97 * @see DeskView 98 */ 99 public class Desktop 100 { 101 /** 102 * Create a desktop using a style determined according to the 103 * user's preferences. 104 */ 105 public Desktop() { 106 this(getPreferredStyle()); 107 } 108 109 /** 110 * New desktop, using preferred style and given context. 111 */ 112 public Desktop(CommandContext ctx) { 113 this(getPreferredStyle(), ctx); 114 } 115 116 117 public Desktop(int style, CommandContext ctx) { 118 commandContext = ctx; 119 120 String val = preferences.getPreference(TTIP_PREF); 121 boolean t = (val == null || val.equalsIgnoreCase("true")); 122 setTooltipsEnabled(t); 123 124 int delay = getTooltipDelay(preferences); 125 setTooltipDelay(delay); 126 127 int duration = getTooltipDuration(preferences); 128 setTooltipDuration(duration); 129 130 String soe = preferences.getPreference(SAVE_ON_EXIT_PREF); 131 setSaveOnExit(soe == null || "true".equalsIgnoreCase(soe)); 132 String rtos = preferences.getPreference(RESTORE_ON_START_PREF); 133 setRestoreOnStart(rtos == null || "true".equalsIgnoreCase(rtos)); // false by default (null) 134 135 File f = getDesktopFile(); 136 firstTime = !(f != null && f.exists()); 137 138 initHelpBroker(); 139 initLAF(ctx); 140 141 142 uif = new UIFactory(getClass(), helpBroker); 143 144 initToolManagers(); 145 146 /* defer view initialization in case we do a restore 147 initView(style); 148 */ 149 this.style = style; 150 } 151 152 private void initLAF(CommandContext ctx) { 153 int preferredLAF; 154 if (ctx != null) { 155 preferredLAF = ctx.getPreferredLookAndFeel(); 156 } else { 157 // can occur from the test 158 preferredLAF = CommandContext.DEFAULT_LAF; 159 } 160 161 switch (preferredLAF) { 162 case CommandContext.SYSTEM_LAF: 163 try { 164 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 165 } catch (Exception e) { 166 } 167 break; 168 case CommandContext.METAL_LAF: 169 try { 170 Class<?> nimbusClass = Class.forName("javax.swing.plaf.metal.MetalLookAndFeel"); 171 UIManager.setLookAndFeel((LookAndFeel) nimbusClass.newInstance()); 172 } catch (Throwable e) { 173 } 174 break; 175 case CommandContext.NIMBUS_LAF: 176 try { 177 Class<?> nimbusClass = Class.forName("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); 178 UIManager.setLookAndFeel((LookAndFeel) nimbusClass.newInstance()); 179 } catch (Throwable e) { 180 } 181 break; 182 // no need to process CommandContext.DEFAULT_LAF - it should equal one of the others constatns 183 } 184 185 } 186 187 /** 188 * Create a desktop using a specified style. 189 * @param style a value indicating the desired desktop style. 190 * @see #MDI_STYLE 191 * @see #SDI_STYLE 192 * @see #TAB_STYLE 193 */ 194 public Desktop(int style) { 195 this(style, null); 196 } 197 198 /** 199 * Get a value indicating the current style of the desktop. 200 * @return a value indicating the current style of the desktop 201 * @see #setStyle 202 * @see #MDI_STYLE 203 * @see #SDI_STYLE 204 * @see #TAB_STYLE 205 */ 206 public int getStyle() { 207 return (currView == null ? style : currView.getStyle()); 208 } 209 210 /** 211 * Get a value indicating the user's preferred desktop style, 212 * as recorded in the user's preferences. 213 * @return a value indicating the user's preferred desktop style 214 * @see #MDI_STYLE 215 * @see #SDI_STYLE 216 * @see #TAB_STYLE 217 */ 218 public static int getPreferredStyle() { 219 // would be better(?) to use classname, perhaps 220 // NO! It must be better to use the tabbed view only. 221 222 return TAB_STYLE; 223 /* SDI/MDI are not supported any more... 224 String prefStyleName = preferences.getPreference(STYLE_PREF); 225 int i = indexOf(prefStyleName, styleNames); 226 return (i != -1 ? i : TAB_STYLE); 227 */ 228 } 229 230 /** 231 * Set the current style of the desktop. 232 * @param style a value indicating the current style of the desktop 233 * @see #getStyle 234 * @see #MDI_STYLE 235 * @see #SDI_STYLE 236 * @see #TAB_STYLE 237 */ 238 public void setStyle(int style) { 239 //System.err.println("Desktop.setStyle: " + style); 240 if (style == getStyle()) 241 return; 242 243 if (currView == null) { 244 this.style = style; 245 return; 246 } 247 248 DeskView oldView = currView; 249 //System.err.println("DT: creating new desktop (" + style + ")"); 250 switch (style) { 251 case MDI_STYLE: 252 currView = new MDIDeskView(oldView); 253 break; 254 255 case SDI_STYLE: 256 currView = new SDIDeskView(oldView); 257 break; 258 259 case TAB_STYLE: 260 currView = new TabDeskView(oldView); 261 break; 262 263 default: 264 throw new IllegalArgumentException(); 265 } 266 267 //System.err.println("DT: disposing old deskview"); 268 oldView.dispose(); 269 270 //System.err.println("DT: setStyle done "); 271 } 272 273 /** 274 * Get the Help Broker used by the Help menu amd context sensitive help. 275 * @return the help broker 276 */ 277 public HelpBroker getHelpBroker() { 278 return helpBroker; 279 } 280 281 /** 282 * Determine if this is the first time that JT Harness has been run. 283 * This is determined by checking if a saved desktop exists from 284 * a prior run of JT Harness. 285 * @return true if this appears to be the first time the user has 286 * run JT Harness, and false otherwise 287 */ 288 public boolean isFirstTime() { 289 return firstTime; 290 } 291 292 /** 293 * Set the flag indicating whether or not this is the first time 294 * that JT Harness has been run. 295 * @param b true if JT Harness should behave as though this is th 296 * first time JT Harness has been run 297 * @see #isFirstTime 298 */ 299 public void setFirstTime(boolean b) { 300 firstTime = b; 301 } 302 303 /** 304 * Check whether the desktop is empty of any tools. 305 * @return true if there are no tools on the desktop, and false otherwise 306 */ 307 public boolean isEmpty() { 308 return (currView == null ? true : currView.isEmpty()); 309 } 310 311 /** 312 * Get the set of tools currently on the desktop. 313 * @return the set of tools currently on the desktop 314 */ 315 public Tool[] getTools() { 316 return (currView == null ? new Tool[0] : currView.getTools()); 317 } 318 319 /** 320 * Add a new tool to the desktop. 321 * @param t the tool to be added 322 * @see #removeTool 323 */ 324 public void addTool(Tool t) { 325 ensureViewInitialized(); 326 currView.addTool(t); 327 } 328 329 /** 330 * Remove a tool from the desktop. 331 * @param t the tool to be removed 332 * @see #addTool 333 */ 334 public void removeTool(Tool t) { 335 if (currView != null) 336 currView.removeTool(t); 337 } 338 339 /** 340 * Get the currently selected tool on the desktop. 341 * @return the currently selected tool on the desktop 342 * @see #setSelectedTool 343 */ 344 public Tool getSelectedTool() { 345 return (currView == null ? null : currView.getSelectedTool()); 346 } 347 348 /** 349 * Set the currently selected tool on the desktop. 350 * @param t the the tool to be selected on the desktop 351 * @see #getSelectedTool 352 */ 353 public void setSelectedTool(Tool t) { 354 ensureViewInitialized(); 355 currView.setSelectedTool(t); 356 } 357 358 /** 359 * Add a new default tool to the desktop. The default can be set via the 360 * system property "javatest.desktop.defaultTool", which should identify 361 * the class name of an appropriate tool manager; if not set, the default 362 * is com.sun.javatest.exec.ExecManager. 363 * @see #removeTool 364 */ 365 public void addDefaultTool() { 366 if (!EventQueue.isDispatchThread()) { 367 invokeOnEventThread(new Runnable() { 368 public void run() { 369 addDefaultTool(); 370 } 371 }); 372 return; 373 } 374 375 for (int i = 0; i < toolManagers.length; i++) { 376 ToolManager m = toolManagers[i]; 377 if (m.getClass().getName().equals(defaultToolManager)) { 378 m.startTool(); 379 return; 380 } 381 } 382 } 383 384 385 /** 386 * Add a new default tool to the desktop. The default can be set via the 387 * system property "javatest.desktop.defaultTool", which should identify 388 * the class name of an appropriate tool manager; if not set, the default 389 * is com.sun.javatest.exec.ExecManager. 390 * @param ip a configuration to be passed to the default tool manager's startTool 391 * method 392 * @see #removeTool 393 */ 394 public Tool addDefaultTool(InterviewParameters ip) { 395 for (int i = 0; i < toolManagers.length; i++) { 396 ToolManager mgr = toolManagers[i]; 397 if (mgr.getClass().getName().equals(defaultToolManager)) { 398 try { 399 // this is to avoid a class dependency to exec package, which is 400 // normally not allowed in this package 401 Method m = mgr.getClass().getMethod("startTool", 402 new Class<?>[] { InterviewParameters.class} ); 403 404 return (Tool) m.invoke(mgr, new Object[] { ip }); 405 } 406 catch (NoSuchMethodException e) { 407 // ignore?? 408 e.printStackTrace(); 409 } 410 catch (IllegalAccessException e) { 411 // ignore?? 412 e.printStackTrace(); 413 } 414 catch (InvocationTargetException e) { 415 // ignore?? 416 unwrap(e).printStackTrace(); 417 } 418 } 419 } 420 return null; 421 } 422 423 /** 424 * Check if a tool is present on the desktop. 425 * @param t the tool for which to check 426 * @return true if the specified tool exists on the desktop, and false otherwise 427 */ 428 public boolean containsTool(Tool t) { 429 Tool[] tools = getTools(); 430 for (int i = 0; i < tools.length; i++) { 431 if (t == tools[i]) 432 return true; 433 } 434 return false; 435 } 436 437 /** 438 * Get the set of tool managers associated with this desktop. 439 * The managers are determined from resource files named 440 * "com.sun.javatest.tool.ToolManager.lst" on the main JT Harness classpath. 441 * @return the set of tool managers associated with this desktop 442 */ 443 public ToolManager[] getToolManagers() { 444 return toolManagers; 445 } 446 447 /** 448 * Get the instance of a tool manager for this desktop of a specific class. 449 * @param c the class of the desired tool manager. 450 * @return a tool manager of the desired type, or null if none found 451 */ 452 public ToolManager getToolManager(Class<?> c) { 453 for (int i = 0; i < toolManagers.length; i++) { 454 ToolManager m = toolManagers[i]; 455 if (c.isInstance(m)) 456 return m; 457 } 458 return null; 459 } 460 461 /** 462 * Get the instance of a tool manager for this desktop of a specific class. 463 * @param className the name of the class of the desired tool manager. 464 * @return a tool manager of the desired type, or null if none found 465 */ 466 public ToolManager getToolManager(String className) { 467 for (int i = 0; i < toolManagers.length; i++) { 468 ToolManager m = toolManagers[i]; 469 if (m.getClass().getName().equals(className)) 470 return m; 471 } 472 return null; 473 } 474 475 /** 476 * Get the top level frames that make up this desktop. TAB and MDI style 477 * desktops just have a single frame; An SDI style desktop may have more 478 * than one frame. 479 * @return the top level frames of this desktop 480 */ 481 public JFrame[] getFrames() { 482 ensureViewInitialized(); 483 return currView.getFrames(); 484 } 485 486 /** 487 * Get a parent component for a dialog to use. 488 * @return Component which can be used as a parent, or null if none 489 * is available. 490 */ 491 public Component getDialogParent() { 492 ensureViewInitialized(); 493 return currView.getDialogParent(); 494 } 495 496 /** 497 * Add a file and a corresponding file opener to the file history 498 * that appears on the File menu. 499 * @param f The file to be added 500 * @param fo A FileOpener object to be used to open the file if necessary 501 */ 502 public void addToFileHistory(File f, FileOpener fo) { 503 // if it is already in the history, remove it 504 for (Iterator<FileHistoryEntry> i = fileHistory.iterator(); i.hasNext(); ) { 505 FileHistoryEntry h = i.next(); 506 if (h.fileOpener == fo && h.file.equals(f)) { 507 i.remove(); 508 break; 509 } 510 } 511 512 // add it to the front of the list 513 fileHistory.addFirst(new FileHistoryEntry(fo, f)); 514 515 // throw away old entries in the list 516 while (fileHistory.size() > FILE_HISTORY_MAX_SIZE) 517 fileHistory.removeLast(); 518 } 519 520 /** 521 * Get a list of the current entries on the file history associated with this desktop. 522 * @return a list of the current entries on the file history associated with this desktop 523 * @see #addToFileHistory 524 */ 525 List<FileHistoryEntry> getFileHistory() 526 { 527 return fileHistory; 528 } 529 530 /** 531 * Check if the top level windows of the desktop are visible or not. 532 * @return true if the top level windows are visible; otherwise, return false 533 * @see #setVisible 534 */ 535 public boolean isVisible() { 536 return (currView == null ? false : currView.isVisible()); 537 } 538 539 /** 540 * Set whether or not the top level windows of the desktop should be visible. 541 * @param b If true, the top level windows will be made visible; if false, they 542 * will be hidden. 543 */ 544 public void setVisible(final boolean b) { 545 if (!EventQueue.isDispatchThread()) { 546 invokeOnEventThread(new Runnable() { 547 public void run() { 548 setVisible(b); 549 } 550 }); 551 return; 552 } 553 554 ensureViewInitialized(); 555 currView.setVisible(b); 556 } 557 558 /** 559 * Create a dialog. 560 * @param tool the parent tool for the dialog 561 * @param uiKey a string which is to be used as the base name for any 562 * resources that may be required 563 * @param title the title for the dialog 564 * @param menuBar the menu bar for the dialog 565 * @param body the body component for the dialog 566 * @param bounds the size and position for the dialog 567 * @return a JDialog or JInternalDialog built from the supplied values. 568 */ 569 public Container createDialog(Tool tool, String uiKey, String title, 570 JMenuBar menuBar, Container body, 571 Rectangle bounds, int type) 572 { 573 ensureViewInitialized(); 574 return currView.createDialog(tool, uiKey, title, menuBar, body, bounds, type); 575 } 576 577 /** 578 * Check if the tool's parent Window is the owner of a dialog. 579 * This may become false if the desktop style is changed after the dialog 580 * was created. 581 * @param tool the tool from which to determine the parent Window 582 * @param dialog the dialog to be checked 583 * @return true if the tool's parent Window is the owner of the dialog, and 584 * false otherwise. 585 */ 586 public boolean isToolOwnerForDialog(Tool tool, Container dialog) { 587 ensureViewInitialized(); 588 return currView.isToolOwnerForDialog(tool, dialog); 589 } 590 591 /** 592 * Check all the tools on the desktop to see if they have open state 593 * that should be saved or processes running. If there is open state 594 * or active processes, a confirmation dialog will be displayed. 595 * If the user confirms OK, or if there was no need to show the 596 * confirmation dialog, the desktop will be saved and disposed. 597 * @param parent A parent frame to be used if a confirmation dialog 598 * is necessary 599 * @see #isOKToExit 600 */ 601 public void checkToolsAndExitIfOK(JFrame parent) { 602 if (isOKToExit(parent)) { 603 if (getSaveOnExit()) 604 save(); 605 606 dispose(); 607 } 608 } 609 610 /** 611 * Check if it is OK to close a tool. If the tool has important 612 * state that needs to be saved, or any processes running, a confirmation 613 * dialog will be shown, to allow the user to cancel the operation if 614 * necessary. 615 * @param t The tool to be checked 616 * @param parent A parent frame to be used if a confirmation dialog 617 * is necessary 618 * @return true if it is OK to close the tool 619 */ 620 public boolean isOKToClose(Tool t, JFrame parent) { 621 if (confirmDialog != null) { 622 Toolkit.getDefaultToolkit().beep(); 623 confirmDialog.toFront(); 624 return false; 625 } 626 627 String[] alerts = t.getCloseAlerts(); 628 if (alerts == null || alerts.length == 0) 629 return true; 630 else 631 return isOKToExitOrClose(parent, alerts, CLOSE); 632 } 633 634 /** 635 * Check if it is OK to close all tools and exit the desktop. 636 * If any tools have important state that needs to be saved, or active tasks 637 * running, a confirmation dialog will be shown to allow the user to 638 * cancel the operation in progress. 639 * @param parent A parent frame to be used if a confirmation dialog 640 * is necessary 641 * @return true if it is OK to exit the desktop, and false otherwise. 642 */ 643 public boolean isOKToExit(JFrame parent) { 644 if (confirmDialog != null) { 645 Toolkit.getDefaultToolkit().beep(); 646 confirmDialog.toFront(); 647 return false; 648 } 649 650 Vector<String> v = new Vector<>(); 651 652 Tool[] tools = getTools(); 653 for (int ti = 0; ti < tools.length; ti++) { 654 String[] alerts = tools[ti].getCloseAlerts(); 655 if (alerts != null) 656 v.addAll(Arrays.asList(alerts)); 657 } 658 659 if (v.size() == 0) 660 return true; 661 else { 662 String[] allAlerts = new String[v.size()]; 663 v.copyInto(allAlerts); 664 return isOKToExitOrClose(parent, allAlerts, EXIT); 665 } 666 } 667 668 private static final int CLOSE = 0; 669 private static final int EXIT = 1; 670 private JDialog confirmDialog; 671 672 private boolean isOKToExitOrClose(JFrame parent, String[] alerts, int mode) { 673 if (confirmDialog != null) { 674 Toolkit.getDefaultToolkit().beep(); 675 confirmDialog.toFront(); 676 return false; 677 } 678 679 Integer m = new Integer(mode); 680 681 if (alerts.length > 0) { 682 // protect against reentrant calls by setting confirmDialog 683 // while showing it 684 685 StringWriter sw = new StringWriter(); 686 try { 687 HTMLWriterEx out = new HTMLWriterEx(sw, uif.getI18NResourceBundle()); 688 out.startTag(HTMLWriterEx.HTML); 689 out.startTag(HTMLWriterEx.HEAD); 690 out.writeContentMeta(); 691 out.endTag(HTMLWriterEx.HEAD); 692 out.startTag(HTMLWriterEx.BODY); 693 out.writeStyleAttr("font-family: SansSerif"); 694 out.startTag(HTMLWriterEx.P); 695 out.writeStyleAttr("margin-top:0"); 696 out.startTag(HTMLWriterEx.B); 697 out.writeI18N("dt.confirm.head", m); 698 out.endTag(HTMLWriterEx.B); 699 out.endTag(HTMLWriterEx.P); 700 out.startTag(HTMLWriterEx.P); 701 out.startTag(HTMLWriterEx.I); 702 out.writeI18N("dt.confirm.warn", m); 703 out.endTag(HTMLWriterEx.I); 704 out.endTag(HTMLWriterEx.P); 705 out.startTag(HTMLWriterEx.UL); 706 out.writeStyleAttr("margin-top:0; margin-bottom:0; margin-left:30"); 707 for (int i = 0; i < alerts.length; i++) { 708 out.startTag(HTMLWriterEx.LI); 709 out.write(alerts[i]); 710 } 711 out.endTag(HTMLWriterEx.UL); 712 out.startTag(HTMLWriterEx.P); 713 out.writeStyleAttr("margin-top:5"); 714 out.writeI18N("dt.confirm.warn2", m); 715 out.startTag(HTMLWriterEx.BR); 716 out.writeI18N("dt.confirm.warn3", m); 717 out.endTag(HTMLWriterEx.P); 718 out.startTag(HTMLWriterEx.P); 719 out.writeStyleAttr("margin-bottom:0"); 720 out.writeI18N("dt.confirm.tail", m); 721 out.endTag(HTMLWriterEx.P); 722 out.endTag(HTMLWriterEx.BODY); 723 out.endTag(HTMLWriterEx.HTML); 724 out.close(); 725 } 726 catch (IOException e) { 727 JavaTestError.unexpectedException(e); 728 } 729 730 JEditorPane body = new JEditorPane(); 731 body.setOpaque(false); 732 body.setContentType("text/html"); 733 body.setText(sw.toString()); 734 body.setEditable(false); 735 body.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 736 //body.setSize(new Dimension(3 * uif.getDotsPerInch(), Integer.MAX_VALUE)); 737 //System.err.println("DT.isOK size=" + body.getSize()); 738 //System.err.println("DT.isOK psize=" + body.getPreferredSize()); 739 String title = uif.getI18NString("dt.confirm.title", m); 740 // can't use JOptionPane convenience methods because we want to set 741 // default option to "No" 742 743 final JOptionPane pane = new JOptionPane(body, JOptionPane.WARNING_MESSAGE); 744 ActionListener l = new ActionListener() { 745 public void actionPerformed(ActionEvent e) { 746 pane.setValue(e.getSource()); 747 pane.setVisible(false); 748 } 749 }; 750 JButton yesBtn = uif.createButton("dt.confirm.yes", l); 751 JButton noBtn = uif.createButton("dt.confirm.no", l); 752 pane.setOptions(new JComponent[] { yesBtn, noBtn }); 753 pane.setInitialValue(noBtn); 754 755 confirmDialog = pane.createDialog(parent, title); 756 confirmDialog.setVisible(true); 757 confirmDialog.dispose(); 758 confirmDialog = null; 759 760 if (pane.getValue() != yesBtn) 761 return false; 762 } 763 764 return true; 765 } 766 767 /** 768 * Check if it is OK to automatically exit JT Harness. 769 * A warning dialog is posted to the user for a reasonable but short while 770 * allowing the user to cancel the exit. 771 * @return true if the user does not respond within the available time, 772 * or if the user allows the request; and false otherwise 773 */ 774 public boolean isOKToAutoExit() { 775 final int delay = 30/*seconds*/; 776 final JTextArea body = new JTextArea(); 777 body.setOpaque(false); 778 body.setText(uif.getI18NString("dt.autoExit.txt", new Integer(delay))); 779 body.setEditable(false); 780 body.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 781 body.setSize(new Dimension(4 * uif.getDotsPerInch(), Integer.MAX_VALUE)); 782 783 final JOptionPane pane = new JOptionPane(body, JOptionPane.WARNING_MESSAGE, JOptionPane.OK_CANCEL_OPTION); 784 String title = uif.getI18NString("dt.confirm.title", new Integer(EXIT)); 785 final JDialog dialog = pane.createDialog(null, title); 786 787 final Timer timer = new Timer(1000, new ActionListener() { 788 public void actionPerformed(ActionEvent e) { 789 if (--timeRemaining == 0) { 790 pane.setValue(new Integer(JOptionPane.OK_OPTION)); 791 dialog.setVisible(false); 792 } 793 else 794 body.setText(uif.getI18NString("dt.autoExit.txt", new Integer(timeRemaining))); 795 } 796 797 private int timeRemaining = delay; 798 }); 799 800 timer.start(); 801 dialog.setVisible(true); 802 timer.stop(); 803 804 Object value = pane.getValue(); 805 return (value != null && value.equals(new Integer(JOptionPane.OK_OPTION))); 806 } 807 808 809 /** 810 * Save the current state of the desktop in the user's standard desktop file. 811 */ 812 public void save() { 813 save(getDesktopFile()); 814 } 815 816 /** 817 * Save the current state of the desktop in a specified desktop file. 818 * @param f the file in which to save the desktop 819 */ 820 public void save(File f) { 821 //System.err.println("DT: save to " + f); 822 if (f == null) 823 return; 824 825 SortedMap<String, String> p = new TreeMap<>(); 826 827 int s = getStyle(); 828 if (s < NUM_STYLES) { 829 p.put("dt.style", styleNames[s]); 830 // backwards compatibility for JT3.1.x 831 p.put("dt.class", jt31StyleClassNames[s]); 832 } 833 834 ensureViewInitialized(); 835 currView.saveDesktop(p); 836 Preferences.access().save(); 837 838 p.put("file.count", String.valueOf(fileHistory.size())); 839 int n = 0; 840 for (Iterator<FileHistoryEntry> i = fileHistory.iterator(); i.hasNext(); ) { 841 FileHistoryEntry h = i.next(); 842 p.put("fileHistory." + n + ".type", h.fileOpener.getFileType()); 843 p.put("fileHistory." + n + ".path", h.file.getPath()); 844 n++; 845 } 846 847 try { 848 File dir = f.getParentFile(); 849 if (dir != null && !dir.exists()) 850 dir.mkdirs(); 851 try (FileOutputStream fos = new FileOutputStream(f); 852 OutputStream out = new BufferedOutputStream(fos)) { 853 Properties.store(p, out, "JT Harness Desktop"); 854 } 855 } 856 catch (IOException e) { 857 System.err.println(uif.getI18NString("dt.cantWriteDt.txt", e.getMessage())); 858 //System.err.println("Error writing desktop file: " + e); 859 } 860 } 861 862 /** 863 * Restore the desktop from information in a saved desktop file. 864 * If no such file exists, or if no tools are successfully started 865 * from the info in the file, add a default tool. 866 * The work will automatically performed on the main AWT EventQueue 867 * thread. 868 */ 869 public void restore() { 870 restore(getDesktopFile()); 871 } 872 873 /** 874 * Restore the desktop from information in a specified file. 875 * If no such file exists, or if no tools are successfully started 876 * from the info in the file, add a default tool. 877 * The work will automatically performed on the main AWT EventQueue 878 * thread. 879 * @param file the file from which to load the data 880 */ 881 public void restore(final File file) { 882 Map<String, String> p = getPreviousDesktop(file); 883 restore0(p); 884 } 885 886 public void restoreHistory() { 887 restoreHistory(getPreviousDesktop(getDesktopFile())); 888 } 889 890 private void restore0(final Map<String, String> p) { 891 //System.err.println("DT: restore " + file); 892 if (!EventQueue.isDispatchThread()) { 893 invokeOnEventThread(new Runnable() { 894 public void run() { 895 restore0(p); 896 } 897 }); 898 return; 899 } 900 901 restoreHistory(p); 902 903 // ALERT!! NEEDS FIXING! 904 // should use saved view info 905 /* 906 String dtClassName = (String) p.getProperty("dt.class"); 907 if (dtClassName != null) { 908 try { 909 if (theOne != null) 910 theOne.dispose(); 911 theOne = (Desktop) (Class.forName(dtClassName).newInstance()); 912 } 913 catch (Throwable e) { 914 // I18N 915 System.err.println("Error loading saved desktop class: " + e); 916 } 917 } 918 */ 919 /* 920 if (currView != null) { 921 style = currView.getStyle(); // set default in case no valid style in desktop file 922 currView.dispose(); 923 } 924 925 int savedStyle; 926 String s = (String) (p.get("dt.style")); 927 if (s != null) 928 savedStyle = indexOf(s, styleNames); 929 else { 930 // javatest 3.1 compatibility 931 String c = (String) (p.get("dt.class")); 932 savedStyle = (c == null ? -1 : indexOf(c, jt31StyleClassNames)); 933 } 934 935 if (savedStyle != -1) 936 style = savedStyle; 937 */ 938 939 ensureViewInitialized(); 940 currView.restoreDesktop(p); 941 942 if (getTools().length == 0) 943 addDefaultTool(); 944 945 // select the previously selected tool, if given 946 // else a default, if available 947 Tool t = getSelectedTool(); 948 if (t == null) { 949 Tool[] tools = getTools(); 950 if (tools.length > 0) 951 t = tools[0]; 952 } 953 if (t != null) 954 setSelectedTool(t); 955 956 //System.err.println("DT.restore: set visible"); 957 setVisible(true); 958 //System.err.println("DT: restore done"); 959 960 } 961 962 private void restoreHistory(Map<String, String> p) { 963 HashMap<String, FileOpener> allOpeners = new HashMap<>(); 964 for (int i = 0; i < toolManagers.length; i++) { 965 ToolManager m = toolManagers[i]; 966 FileOpener[] mgrOpeners = m.getFileOpeners(); 967 if (mgrOpeners != null) { 968 for (int j = 0; j < mgrOpeners.length; j++) { 969 FileOpener fo = mgrOpeners[j]; 970 allOpeners.put(fo.getFileType(), fo); 971 } 972 } 973 } 974 975 try { 976 fileHistory.clear(); 977 String c = p.get("file.count"); 978 if (c != null) { 979 int count = Integer.parseInt(c); 980 for (int i = 0; i < count; i++) { 981 try { 982 String ft = p.get("fileHistory." + i + ".type"); 983 FileOpener fo = allOpeners.get(ft); 984 if (fo != null) { 985 String path = p.get("fileHistory." + i + ".path"); 986 if (path != null && path.length() > 0) 987 fileHistory.add(new FileHistoryEntry(fo, new File(path))); 988 } 989 } 990 catch (Throwable e) { 991 // I18N 992 //System.err.println("Error loading saved file: " + e); 993 System.err.println(uif.getI18NString("dt.cantLoadHist.txt")); 994 e.printStackTrace(); 995 } 996 } 997 } 998 } 999 catch (NumberFormatException ignore) { 1000 // ignore, for now 1001 } 1002 } 1003 1004 static Map<String, String> getPreviousDesktop(File file) { 1005 if (file == null) 1006 file = getDesktopFile(); 1007 1008 Map<String, String> stringPropsMap = new HashMap<>(); 1009 1010 if (file != null && file.exists()) { 1011 try (FileInputStream fis = new FileInputStream(file); 1012 InputStream in = new BufferedInputStream(fis)) { 1013 stringPropsMap = Properties.load(in); 1014 } 1015 catch (IOException e) { 1016 // I18N 1017 System.err.println("Error reading desktop file: " + e); 1018 } 1019 } 1020 1021 return stringPropsMap; 1022 } 1023 1024 /** 1025 * Show a Preferences window. 1026 * @param parent the parent frame to be used for the preferences dialog 1027 */ 1028 public void showPreferences(JFrame parent) { 1029 if (prefsPane == null) 1030 prefsPane = new DesktopPrefsPane(this, uif); 1031 if(colorPane == null) 1032 colorPane = new ColorPrefsPane(uif); 1033 1034 Vector<PreferencesPane> v = new Vector<>(); 1035 v.addElement(prefsPane); 1036 v.addElement(colorPane); 1037 for (int i = 0; i < toolManagers.length; i++) { 1038 ToolManager m = toolManagers[i]; 1039 PreferencesPane p = m.getPrefsPane(); 1040 if (p != null) 1041 v.addElement(p); 1042 } 1043 1044 PreferencesPane[] custom = getCustomPreferences(); 1045 if (custom != null) 1046 for (int i = 0; i < custom.length; i++) 1047 v.add(custom[i]); 1048 1049 PreferencesPane[] panes = new PreferencesPane[v.size()]; 1050 v.copyInto(panes); 1051 PreferencesPane.showDialog(parent, preferences, panes, helpBroker); 1052 } 1053 1054 /** 1055 * Allow for other custom prefs panes. Current implementation scans 1056 * test suite properties for one called "prefsPane", which should be a 1057 * class name referring to a class which subclasses <code>Preferences.Pane</code>. 1058 * That class will be loaded and instantiated using the test suite's class loader. 1059 * @return A set of prefs panes, beyond that of the currently active Tools. 1060 * Null if none. 1061 */ 1062 private PreferencesPane[] getCustomPreferences() { 1063 List<PreferencesPane> al = new ArrayList<>(); 1064 1065 Set<String> customPrefsClasses = new HashSet<>(); 1066 1067 Tool[] tools = getTools(); 1068 for (int i = 0; i < tools.length; i++) { 1069 TestSuite[] tss = tools[i].getLoadedTestSuites(); 1070 if (tss != null && tss.length > 0) { 1071 for (int j = 0; j < tss.length; j++) { 1072 // only process each test suite once 1073 if (customPrefsClasses.contains(tss[j].getID())) 1074 continue; 1075 else 1076 customPrefsClasses.add(tss[j].getID()); 1077 1078 String cls = tss[j].getTestSuiteInfo("prefsPane"); 1079 try { 1080 if (cls != null) { 1081 PreferencesPane pane = 1082 (PreferencesPane)((Class.forName(cls, true, 1083 tss[j].getClassLoader())).newInstance()); 1084 al.add(pane); 1085 } 1086 } 1087 catch (ClassNotFoundException e) { 1088 e.printStackTrace(); // XXX rm 1089 // should print log entry 1090 } 1091 catch (InstantiationException e) { 1092 e.printStackTrace(); // XXX rm 1093 // should print log entry 1094 } 1095 catch (IllegalAccessException e) { 1096 e.printStackTrace(); // XXX rm 1097 // should print log entry 1098 } // try 1099 finally { 1100 } 1101 } // inner for j 1102 } 1103 } // for i 1104 1105 if (al.size() > 0) { 1106 PreferencesPane[] panes = new PreferencesPane[al.size()]; 1107 al.toArray(panes); 1108 return panes; 1109 } 1110 else 1111 return null; 1112 } 1113 1114 /** 1115 * Get an icon containing the JT Harness logo. 1116 * @return an icon containing the JT Harness logo 1117 */ 1118 public Icon getLogo() { 1119 return uif.createIcon("dt.logo"); 1120 } 1121 1122 /** 1123 * Dispose of any resources used by this object. 1124 */ 1125 public void dispose() { 1126 if (currView != null) 1127 currView.dispose(); 1128 } 1129 1130 /** 1131 * Print a text message to the desktop logfile. 1132 * A single line of text which is as short as possible is highly 1133 * recommended for readability purposes. 1134 * 1135 * @param i18n a resource bundle containing the localized messages 1136 * @param key a key into the resource bundle for the required message 1137 * 1138 * @since 3.0.1 1139 */ 1140 public void log(I18NResourceBundle i18n, String key) { 1141 ensureLogFileInitialized(); 1142 logFile.log(i18n, key); 1143 } 1144 1145 /** 1146 * Print a text message to the desktop logfile. 1147 * A single line of text which is as short as possible is highly 1148 * recommended for readability purposes. 1149 * 1150 * @param i18n a resource bundle containing the localized messages 1151 * @param key a key into the resource bundle for the required message 1152 * @param arg An argument to be formatted into the specified message. 1153 * If this is a <code>Throwable</code>, its stack trace 1154 * will be included in the log. 1155 * @since 3.0.1 1156 */ 1157 public void log(I18NResourceBundle i18n, String key, Object arg) { 1158 ensureLogFileInitialized(); 1159 logFile.log(i18n, key, arg); 1160 } 1161 1162 /** 1163 * Print a text message to the desktop logfile. 1164 * A single line of text which is as short as possible is highly 1165 * recommended for readability purposes. 1166 * 1167 * @param i18n a resource bundle containing the localized messages 1168 * @param key a key into the resource bundle for the required message 1169 * @param args An array of arguments to be formatted into the specified message. 1170 * If the first arg is a <code>Throwable</code>, its stack 1171 * trace will be included in the log. 1172 * @since 3.0.1 1173 */ 1174 public void log(I18NResourceBundle i18n, String key, Object[] args) { 1175 ensureLogFileInitialized(); 1176 logFile.log(i18n, key, args); 1177 } 1178 1179 private void ensureLogFileInitialized() { 1180 if (logFile == null) { 1181 File f; 1182 String s = System.getProperty("javatest.desktop.log"); 1183 if (s == null) { 1184 File jtDir = Preferences.getPrefsDir(); 1185 f = new File(jtDir, "log.txt"); 1186 } 1187 else if (s.equals("NONE")) { 1188 f = null; 1189 } 1190 else 1191 f = new File(s); 1192 1193 try { 1194 BackupPolicy p = BackupPolicy.simpleBackups(5); 1195 p.backup(f); 1196 } 1197 catch (IOException e) { 1198 // ignore? or save exception to write to logFile 1199 } 1200 1201 logFile = (f == null ? new LogFile() : new LogFile(f)); 1202 } 1203 } 1204 1205 // the order of the styles is the presentation order in the preferences panel 1206 /** 1207 * A constant to indicate the tabbed-style desktop: 1208 * a single window for the desktop, using a tabbed pane for the tools. 1209 */ 1210 public static final int TAB_STYLE = 0; 1211 1212 /** 1213 * A constant to indicate the MDI-style desktop: 1214 * a single window for the desktop, containing multiple internal windows, one per tool. 1215 */ 1216 public static final int MDI_STYLE = 1; 1217 1218 /** 1219 * A constant to indicate the SDI-style desktop: 1220 * multiple top-level windows, one per tool. 1221 */ 1222 public static final int SDI_STYLE = 2; 1223 1224 static final int NUM_STYLES = 3; 1225 static final String[] styleNames = {"tab", "mdi", "sdi"}; 1226 1227 private static final String[] jt31StyleClassNames = { 1228 "com.sun.javatest.tool.TabDesktop", 1229 "com.sun.javatest.tool.MDIDesktop", 1230 "com.sun.javatest.tool.SDIDesktop" 1231 }; 1232 1233 /** 1234 * Check whether or not the desktop will save its state when the VM exits. 1235 * @return true if the desktop will save its state when the VM exits, and false otherwise 1236 * @see #setSaveOnExit 1237 */ 1238 public boolean getSaveOnExit() { 1239 return saveOnExit; 1240 } 1241 1242 /** 1243 * Specify whether or not the desktop will save its state when the VM exits. 1244 * @param b true if the desktop should save its state when the VM exits, and false otherwise 1245 * @see #getSaveOnExit 1246 */ 1247 public void setSaveOnExit(boolean b) { 1248 saveOnExit = b; 1249 } 1250 1251 /** 1252 * Check whether or not the desk view should restore saved tools state when the Harness is starting. 1253 * @return true if the desk will restore its tools when the Harness is starting, and false otherwise 1254 * @see #setRestoreOnStart(boolean) 1255 */ 1256 public boolean getRestoreOnStart() { 1257 return restoreOnStart; 1258 } 1259 1260 /** 1261 * Specify whether or not the desk view should restore saved tools state when the Harness is starting. 1262 * @param restoreOnStart true if the desk will restore its tools when the Harness is starting, and false otherwise 1263 * @see #getRestoreOnStart() 1264 */ 1265 public void setRestoreOnStart(boolean restoreOnStart) { 1266 this.restoreOnStart = restoreOnStart; 1267 } 1268 1269 /** 1270 * Get Tooltip delay from prefs in ms. 1271 * @return Range is 0-Integer.MAX_VALUE 1272 */ 1273 static int getTooltipDelay(Preferences p) { 1274 String val = p.getPreference(TTIP_DELAY); 1275 int result = TTIP_DELAY_DEFAULT; 1276 1277 try { 1278 // expected range from prefs in 0-Integer.MAX_VALUE 1279 result = Integer.parseInt(val); 1280 } 1281 catch (NumberFormatException e) { 1282 // default to no delay 1283 result = TTIP_DELAY_DEFAULT; 1284 } 1285 1286 if (result < 0) 1287 result = TTIP_DELAY_DEFAULT;; 1288 1289 return result; 1290 } 1291 1292 /** 1293 * Get tooltip duration from prefs in ms. 1294 * This is the translated value, so the "forever" value has been 1295 * transformed into something useful. 1296 * @return Range is 0-Integer.MAX_VALUE 1297 */ 1298 static int getTooltipDuration(Preferences p) { 1299 String val = p.getPreference(TTIP_DURATION); 1300 int result = TTIP_DURATION_DEFAULT; 1301 1302 try { 1303 // expected range from prefs in -1-Integer.MAX_VALUE 1304 result = Integer.parseInt(val); 1305 } 1306 catch (NumberFormatException e) { 1307 // default to no delay 1308 result = TTIP_DURATION_DEFAULT; 1309 } 1310 1311 if (result < 0) 1312 if (result == TTIP_DURATION_FOREVER) // indicates forever duration 1313 result = Integer.MAX_VALUE; 1314 else // -2 or less, unknown value 1315 result = TTIP_DURATION_DEFAULT; 1316 else { } 1317 1318 return result; 1319 } 1320 1321 // these are here to be shared with DesktopPrefsPane. 1322 void setTooltipsEnabled(boolean state) { 1323 ToolTipManager.sharedInstance().setEnabled(state); 1324 } 1325 1326 /** 1327 * Unconditionally set the tooltip delay to the given setting. 1328 * @param delay Delay time in ms or TTIP_DELAY_NONE. 1329 */ 1330 void setTooltipDelay(int delay) { 1331 ToolTipManager.sharedInstance().setInitialDelay(delay); 1332 } 1333 1334 /** 1335 * Unconditionally set the tooltip duration to the given setting. 1336 * @param duration Duration time in ms or TTTIP_DURATION_FOREVER. 1337 */ 1338 void setTooltipDuration(int duration) { 1339 if (duration == TTIP_DURATION_FOREVER) 1340 ToolTipManager.sharedInstance().setDismissDelay(Integer.MAX_VALUE); 1341 else 1342 ToolTipManager.sharedInstance().setDismissDelay(duration); 1343 } 1344 1345 public void printSetup() { 1346 ensurePrintAttrsInitialized(); 1347 PrinterJob job = PrinterJob.getPrinterJob(); 1348 job.pageDialog(printAttrs); 1349 } 1350 1351 public void print(Printable printable) { 1352 1353 DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE; 1354 PrintService[] services = PrintServiceLookup.lookupPrintServices(flavor, null); 1355 1356 if(services.length > 0) { 1357 ensurePrintAttrsInitialized(); 1358 1359 Component parent = getDialogParent(); 1360 int x = (int)parent.getLocationOnScreen().getX() + parent.getWidth() / 2 - 250; 1361 int y = (int)parent.getLocationOnScreen().getY() + parent.getHeight() / 2 - 250; 1362 1363 PrintService service = ServiceUI.printDialog(null, x, y, services, 1364 services[0], flavor, printAttrs); 1365 if(service != null) { 1366 DocPrintJob job = service.createPrintJob(); 1367 try { 1368 Doc doc = new SimpleDoc(printable, flavor, null); 1369 1370 job.print(doc, printAttrs); 1371 } 1372 catch (PrintException e) { 1373 e.printStackTrace(); 1374 } 1375 } 1376 } 1377 1378 } 1379 1380 private void ensurePrintAttrsInitialized() { 1381 if(printAttrs == null) { 1382 printAttrs = new HashPrintRequestAttributeSet(); 1383 } 1384 } 1385 1386 private void initHelpBroker() { 1387 URL u = null; // URL for help set 1388 ClassLoader theLoader = null; 1389 1390 if (commandContext != null) { 1391 final Command[] cmds = commandContext.getCommands(); 1392 // uses the first custom help loader found. 1393 // use a customized HelpBroker that will exit the VM when closed. 1394 for (int i = 0; i < cmds.length; i++) { 1395 theLoader = cmds[i].getCustomHelpLoader(); 1396 // could also upgrade this to accept a different help set name 1397 u = HelpSet.findHelpSet(theLoader, "jthelp.hs"); 1398 if (u != null) 1399 break; 1400 } // for 1401 } 1402 1403 if (u == null) { 1404 // could also upgrade this to accept a different help set name 1405 theLoader = this.getClass().getClassLoader(); 1406 u = HelpSet.findHelpSet(theLoader, "com/sun/javatest/help/jthelp.hs"); 1407 } 1408 1409 if (u != null) { 1410 //HelpSet helpSet = new HelpSet(theLoader, u); 1411 //helpBroker = helpSet.createHelpBroker(); 1412 helpBroker = new JTHelpBroker(); 1413 1414 // Not safe to run initPresentation in a background 1415 // thread, despite the JavaHelp specification. 1416 // so run it synchronously instead. 1417 // don't even run it synchronously 1418 //helpBroker.initPresentation(); 1419 /* 1420 // launch background thread to init help in the 1421 // background, per JavaHelp suggestions 1422 Runnable r = new Runnable() { 1423 public void run() { 1424 helpBroker.initPresentation(); 1425 } 1426 }; 1427 Thread t = new Thread(r); 1428 t.setPriority(Thread.MIN_PRIORITY + 1); // not lowest, but pretty low 1429 t.start(); 1430 * 1431 */ 1432 } 1433 } 1434 1435 private void initToolManagers() { 1436 // locate init file and load up the managers 1437 //System.err.println("Desktop.initToolManagers"); 1438 1439 try { 1440 ManagerLoader ml = new ManagerLoader(ToolManager.class, System.err); 1441 ml.setManagerConstructorArgs(new Class<?>[] { Desktop.class }, new Object[] { this }); 1442 Set<?> s = ml.loadManagers(TOOLMGRLIST); 1443 toolManagers = s.toArray(new ToolManager[s.size()]); 1444 } 1445 catch (IOException e) { 1446 throw new JavaTestError(uif.getI18NResourceBundle(), 1447 "dt.cantAccessResource", new Object[] { TOOLMGRLIST, e } ); 1448 } 1449 } 1450 1451 private void ensureViewInitialized() { 1452 if (currView != null) 1453 return; 1454 1455 switch (style) { 1456 case MDI_STYLE: 1457 currView = new MDIDeskView(this); 1458 break; 1459 1460 case SDI_STYLE: 1461 currView = new SDIDeskView(this); 1462 break; 1463 1464 default: 1465 currView = new TabDeskView(this); 1466 break; 1467 } 1468 } 1469 1470 private static void appendStrings(StringBuffer sb, String[] msgs) { 1471 if (msgs != null) { 1472 for (int i = 0; i < msgs.length; i++) { 1473 sb.append(msgs[i]); 1474 if (!msgs[i].endsWith("\n")) 1475 sb.append('\n'); 1476 } 1477 } 1478 } 1479 1480 /** 1481 * Get the file in which the desktop is (to be) stored. 1482 * The standard location is the platform equivalent of 1483 * $HOME/.javatest/desktop 1484 * It can be overridden by setting the system property 1485 * "javatest.desktop.file", which can be set to "NONE" 1486 * to disable the feature. 1487 */ 1488 private static File getDesktopFile() { 1489 String s = System.getProperty("javatest.desktop.file"); 1490 if (s == null) { 1491 File jtDir = Preferences.getPrefsDir(); 1492 return new File(jtDir, "desktop"); 1493 } 1494 else if (!s.equals("NONE")) 1495 return new File(s); 1496 else 1497 return null; 1498 } 1499 1500 static void addHelpDebugListener(Component c) { 1501 JComponent root; 1502 if (c instanceof JFrame) 1503 root = ((JFrame) c).getRootPane(); 1504 else if (c instanceof JDialog) 1505 root = ((JDialog) c).getRootPane(); 1506 else 1507 throw new IllegalArgumentException(); 1508 1509 ActionListener showFocusListener = new ActionListener() { 1510 public void actionPerformed(ActionEvent e) { 1511 Component src = (Component) e.getSource(); 1512 Component comp = javax.swing.SwingUtilities.findFocusOwner(src); 1513 System.err.println("ALT-F2: source=" + src); 1514 System.err.println("ALT-F2: focus=" + comp); 1515 String helpId = comp == null ? "(none)" : ContextHelpManager.getHelpIDString(comp); 1516 helpId = helpId == null ? ContextHelpManager.getHelpIDString(src) : helpId; 1517 System.err.println("ALT-F2: helpId=" + helpId); 1518 } 1519 }; 1520 1521 root.registerKeyboardAction(showFocusListener, 1522 KeyStroke.getKeyStroke("alt F2"), 1523 JComponent.WHEN_IN_FOCUSED_WINDOW); 1524 } 1525 1526 static void addPreferredSizeDebugListener(Component c) { 1527 JComponent root; 1528 if (c instanceof JFrame) 1529 root = ((JFrame) c).getRootPane(); 1530 else if (c instanceof JDialog) 1531 root = ((JDialog) c).getRootPane(); 1532 else 1533 throw new IllegalArgumentException(); 1534 1535 ActionListener showPrefSizeListener = new ActionListener() { 1536 public void actionPerformed(ActionEvent e) { 1537 Component src = (Component) e.getSource(); 1538 Component c = javax.swing.SwingUtilities.findFocusOwner(src); 1539 while (c != null) { 1540 Dimension d = c.getPreferredSize(); 1541 System.err.println("ALT-1: comp=" + c.getName() + "(" + c.getClass().getName() + ") " 1542 + "[w:" + d.width + ",h:" + d.height + "]"); 1543 c = c.getParent(); 1544 } 1545 } 1546 }; 1547 1548 root.registerKeyboardAction(showPrefSizeListener, 1549 KeyStroke.getKeyStroke("alt 1"), 1550 JComponent.WHEN_IN_FOCUSED_WINDOW); 1551 } 1552 1553 private static void invokeOnEventThread(Runnable r) { 1554 try { 1555 EventQueue.invokeAndWait(r); 1556 } 1557 catch (InterruptedException e) { 1558 } 1559 catch (InvocationTargetException e) { 1560 Throwable t = e.getTargetException(); 1561 if (t instanceof RuntimeException) 1562 throw ((RuntimeException) t); 1563 else 1564 throw ((Error) t); 1565 } 1566 } 1567 1568 private static int indexOf(String s, String[] a) { 1569 for (int i = 0; i < a.length; i++) { 1570 if (s == null ? a[i] == null : s.equals(a[i])) 1571 return i; 1572 } 1573 return -1; 1574 } 1575 1576 /** 1577 * Walks down the exception causes until the original exception is found. 1578 * @param t The problem object to decend into. 1579 * @return The original problem object. 1580 */ 1581 private static Throwable unwrap(Throwable t) { 1582 if (t == null) { 1583 return t; 1584 } 1585 1586 Throwable t1; 1587 while (true) { 1588 t1 = t.getCause(); 1589 if (t1==null) { 1590 return t; 1591 } else { 1592 t = t1; 1593 } 1594 } 1595 } 1596 1597 private final UIFactory uif; 1598 private CommandContext commandContext; 1599 private DeskView currView; 1600 private int style; // used until currView is set, then style comes from that 1601 private PreferencesPane prefsPane; 1602 private PreferencesPane colorPane; 1603 private ToolManager[] toolManagers; 1604 private HelpBroker helpBroker; 1605 private LogFile logFile; 1606 private boolean firstTime; 1607 private boolean saveOnExit; 1608 private PrintRequestAttributeSet printAttrs; 1609 private boolean restoreOnStart; 1610 1611 private LinkedList<FileHistoryEntry> fileHistory = new LinkedList<>(); 1612 private static final int FILE_HISTORY_MAX_SIZE = 10; 1613 1614 private static Preferences preferences = Preferences.access(); 1615 1616 1617 static final String STYLE_PREF = "tool.appearance.style"; 1618 static final String TTIP_PREF = "tool.appearance.ttipToggle"; 1619 static final String TTIP_DELAY= "tool.appearance.ttipDelay"; 1620 static final String TTIP_DURATION = "tool.appearance.ttipDuration"; 1621 static final int TTIP_DURATION_FOREVER = -1; 1622 static final int TTIP_DELAY_NONE = 0; 1623 static final int TTIP_DELAY_DEFAULT = 0; 1624 static final int TTIP_DURATION_DEFAULT = 5000; 1625 static final String SAVE_ON_EXIT_PREF = "tool.appearance.saveOnExit"; 1626 static final String RESTORE_ON_START_PREF = "tool.appearance.restoreOnStart"; 1627 1628 private static final String TOOLMGRLIST = "META-INF/services/com.sun.javatest.tool.ToolManager.lst"; 1629 private static final String defaultToolManager = 1630 System.getProperty("javatest.desktop.defaultToolManager", "com.sun.javatest.exec.ExecToolManager"); 1631 1632 1633 //------------------------------------------------------------------------- 1634 1635 /** 1636 * A class for an entry on the file history list. 1637 * It defines a file, and an object to open that file if required. 1638 */ 1639 static class FileHistoryEntry { 1640 FileHistoryEntry(FileOpener fo, File f) { 1641 fileOpener = fo; 1642 file = f; 1643 } 1644 1645 FileOpener fileOpener; 1646 File file; 1647 } 1648 }