1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2010, 2012, 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.exec; 28 29 import com.sun.interview.Help; 30 import java.awt.CardLayout; 31 import java.awt.Component; 32 import java.awt.Container; 33 import java.awt.Dimension; 34 import java.awt.AWTEvent; 35 import java.awt.FocusTraversalPolicy; 36 import java.awt.KeyboardFocusManager; 37 import java.awt.event.ActionEvent; 38 import java.awt.event.ActionListener; 39 import java.io.File; 40 import java.io.FileNotFoundException; 41 import java.io.IOException; 42 import java.util.HashMap; 43 import java.util.Iterator; 44 import java.util.Map; 45 import java.util.Set; 46 import java.util.TreeSet; 47 import javax.swing.BorderFactory; 48 import javax.swing.ButtonGroup; 49 import javax.swing.JCheckBoxMenuItem; 50 import javax.swing.JComponent; 51 import javax.swing.JFileChooser; 52 import javax.swing.JMenu; 53 import javax.swing.JMenuBar; 54 import javax.swing.JMenuItem; 55 import javax.swing.JOptionPane; 56 import javax.swing.JPanel; 57 import javax.swing.JRadioButtonMenuItem; 58 import javax.swing.JSplitPane; 59 import javax.swing.KeyStroke; 60 import javax.swing.WindowConstants; 61 import javax.swing.event.ChangeEvent; 62 import javax.swing.event.ChangeListener; 63 import javax.swing.event.MenuEvent; 64 import javax.swing.event.MenuListener; 65 66 import com.sun.interview.Interview; 67 import com.sun.interview.Question; 68 import com.sun.javatest.InterviewParameters; 69 import com.sun.javatest.TestSuite; 70 import com.sun.javatest.WorkDirectory; 71 import com.sun.javatest.exec.WorkDirChooseTool.ExecModelStub; 72 import com.sun.javatest.tool.FileChooser; 73 import com.sun.javatest.tool.FileHistory; 74 import com.sun.javatest.tool.HelpLink; 75 import com.sun.javatest.tool.Preferences; 76 import com.sun.javatest.tool.ToolDialog; 77 import com.sun.javatest.tool.UIFactory; 78 import com.sun.javatest.tool.jthelp.HelpID; 79 import com.sun.javatest.tool.jthelp.JHelpContentViewer; 80 import com.sun.javatest.util.Debug; 81 import java.util.ArrayList; 82 import java.util.List; 83 84 /** 85 * Dialog to edit InterviewParameters object. 86 * 87 * InterviewEditor keeps reference to the main InterviewParameters object, 88 * but never change it. 89 * <br> 90 * Before editing interview the main InterviewParameters object is synced 91 * with the view object. 92 * <br> 93 * When view object is loaded or saved, all registered observers are notified. 94 * 95 */ 96 public class InterviewEditor extends ToolDialog { 97 98 public InterviewEditor(JComponent parent, UIFactory uif, 99 InterviewParameters ip) { 100 101 super(parent, uif, "ce", ToolDialog.MODAL_DOCUMENT); 102 WorkDirectory wd = ip.getWorkDirectory(); 103 if (wd == null) { 104 throw new IllegalArgumentException(uif.getI18NString("ce.wdNull.err")); 105 } 106 mainConfig = ip; 107 try { 108 //this.key = key; 109 this.ext = getExtention(); 110 viewConfig = ip.getTestSuite().createInterview(); 111 viewConfig.setWorkDirectory(ip.getWorkDirectory()); 112 history = FileHistory.getFileHistory(wd, getHistoryFileName()); 113 } catch (TestSuite.Fault e) { 114 // ignore, for now; should not happen 115 } 116 } 117 public InterviewEditor(JComponent parent, UIFactory uif, 118 InterviewParameters ip, ContextManager cm) { 119 this(parent, uif, ip); 120 setContextManager(cm); 121 } 122 123 /** 124 * Sets contextManager to the passed value. 125 * @param cm - ContextManager to use 126 */ 127 void setContextManager(ContextManager cm) { 128 this.contextManager = cm; 129 } 130 131 /** 132 * Returns previously created or set ContextManager. If not set, creates 133 * the new instance. 134 */ 135 ContextManager getContextManager() { 136 if (contextManager == null) { 137 try { 138 contextManager = (ContextManager) ((Class.forName( 139 "com.sun.javatest.exec.ContextManager")).newInstance()); 140 } catch (Exception e) { 141 e.printStackTrace(); 142 } 143 } 144 return contextManager; 145 } 146 147 148 /** 149 * Returns extension for files to be saved. Subclasses like TemplateEditor 150 * might override this method. 151 * 152 * @return default extension 153 */ 154 protected String getExtention() { 155 return CONFIG_EXTENSION; 156 } 157 158 /** 159 * Returns file name to store history of configuration files. 160 * This implementation returns "configHistory.jtl". Subclasses might 161 * override this method to return alternative value. 162 */ 163 protected String getHistoryFileName() { 164 return CONFIG_HISTORY; 165 } 166 167 private void setRestorer(CE_View view) { 168 getRestorer().setRestorePolicy(Restorer.RESTORE_ALL); 169 getRestorer().setWindowKey(getRestorerWindowKey(view == fullView)); 170 } 171 172 protected String getRestorerWindowKey(boolean isFullView) { 173 return "confEdit.config" + (isFullView ? ".f" : ".s"); 174 } 175 176 /** 177 * Adds passed file to the history. 178 * @param f - file to be added. 179 */ 180 void addToHistory(File f) { 181 if (history == null) { 182 WorkDirectory wd = viewConfig.getWorkDirectory(); 183 if (wd == null) { 184 return; 185 } 186 history = FileHistory.getFileHistory(wd, getHistoryFileName()); 187 } 188 history.add(f); 189 } 190 191 /** 192 * Syncs mainConfig and viewConfig 193 */ 194 private void sync() { 195 try { 196 copy(mainConfig, viewConfig); 197 } catch (Interview.Fault e) { 198 uif.showError("ce.show.error", e.getMessage()); 199 } 200 } 201 202 /** 203 * Starts editing new config. Supposed to be called outside. 204 */ 205 public void newConfig() { 206 try { 207 InterviewParameters newConfig = viewConfig.getTestSuite().createInterview(); 208 copy(newConfig, viewConfig); 209 } catch (TestSuite.Fault e) { 210 uif.showError("ce.show.error", e.getMessage()); 211 } catch (Interview.Fault e) { 212 uif.showError("ce.show.error", e.getMessage()); 213 } 214 show(FULL_MODE); 215 } 216 217 /** 218 * Start editing empty configuration. 219 */ 220 private void newConfigAsk() { 221 if (askAndSave("ce.clear.warn")) { 222 newConfig(); 223 } 224 } 225 /** 226 * Show dialog. 227 */ 228 public void edit(int mode) { 229 sync(); 230 show(mode); 231 } 232 233 /** 234 * @return mode that will be used by WorkDirChooseTool to select file. 235 */ 236 public int getFileChooserMode() { 237 return WorkDirChooseTool.LOAD_CONFIG; 238 } 239 240 /** 241 * Show choose file dialog and then load new file. 242 * Supposed to be invoked from outside of editor. 243 * Doesn't expect that viewConfig can be changed. 244 */ 245 public void loadConfig() { 246 loadConfig0(false); 247 } 248 249 /** 250 * Show choose file dialog and then load new file. 251 * The dialog depends on fileChooserMode setting. It can be either 252 * simple JFileChooser or "advanced" home made file chooser. 253 * @param ask if true, dialog asking whether to save changes will appear 254 * in case of unsaved changes. 255 */ 256 protected void loadConfig0(boolean ask) { 257 TestSuite ts = viewConfig.getTestSuite(); 258 ExecModelStub em = new ExecModelStub(ts, contextManager); 259 try { 260 em.setWorkDir(mainConfig.getWorkDirectory(), true); 261 } catch (Exception ignore) { 262 // stub never throws exceptions 263 } 264 WorkDirChooseTool fc = WorkDirChooseTool.getTool((JComponent)parent, 265 uif, em, getFileChooserMode(), ts, true); 266 WorkDirChooseTool.ChosenFileHandler cfh = 267 new WorkDirChooseTool.ChosenFileHandler(); 268 fc.setChosenFileHandler(cfh); 269 fc.doTool(); 270 271 File f = cfh.file; 272 if (f != null && (!ask || askAndSave("ce.load.warn"))) { 273 loadConfigFromFile(f); 274 if (cfh.isEditConfig && !isVisible()) { 275 edit(FULL_MODE); 276 } 277 } 278 } 279 280 /** 281 * Works similar to loadConfig(), but asks to save changes if any 282 * before reload. 283 */ 284 private void loadConfigAsk() { 285 loadConfig0(true); 286 } 287 288 /** 289 * 290 * @param f 291 */ 292 public void loadAndEdit(File f) { 293 if (f == null) { 294 return; 295 } 296 loadConfigFromFile(f); 297 notifyObservers(); 298 show(FULL_MODE); 299 } 300 301 /** 302 * Shows file chooser dialog. 303 * @return chosen file or null. 304 */ 305 private File chooseConfigFile() { 306 File mainConfigFile = mainConfig.getFile(); 307 FileChooser fileChooser = getFileChooser(); 308 if (mainConfigFile != null) 309 fileChooser.setCurrentDirectory(mainConfigFile.getParentFile()); 310 return loadConfigFile(getContextManager(), parent, uif, fileChooser); 311 } 312 313 /** 314 * Updates viewConfig, notifies observers of the change. 315 * @param file File to load. 316 */ 317 public void loadConfigFromFile(File file) { 318 if (file == null) { 319 return; 320 } 321 try { 322 viewConfig.load(file); 323 if (currView != null && currView.isShowing()) 324 currView.load(); 325 addToHistory(file); 326 updateTitle(); 327 notifyObservers(); 328 } catch (FileNotFoundException e) { 329 uif.showError("ce.load.cantFindFile", file); 330 } catch (IOException e) { 331 uif.showError("ce.load.error", new Object[] { file, e } ); 332 } catch (Interview.Fault e) { 333 uif.showError("ce.load.error", new Object[] { file, e.getMessage() } ); 334 } 335 } 336 337 338 public void save() { 339 save0(); 340 } 341 342 // return true if saved, false if cancelled/error 343 private boolean save0() { 344 // Use the filename saved in the viewConfig. 345 // By default, this will have been copied from the mainConfig, 346 // but it may have been cleared if clear() has been called, thereby 347 // making "save" behave as "saveAs". 348 return save0(viewConfig.getFile()); 349 } 350 351 public void saveAs() { 352 save0(null); 353 } 354 355 // return true if saved, false if cancelled/error 356 private boolean save0(File file) { 357 if (file == null) { 358 File mainConfigFile = mainConfig.getFile(); 359 File mainConfigDir = (mainConfigFile == null ? null : mainConfigFile.getParentFile()); 360 file = getSaveFile(mainConfigDir); 361 if (file == null) 362 return false; // exit without saving 363 } 364 365 try { 366 if (currView != null) { 367 currView.save(); 368 } 369 viewConfig.setFile(file); // for subsequent use 370 doSave(file); 371 addToHistory(file); 372 373 updateTitle(); 374 notifyObservers(); 375 376 return true; 377 } 378 catch (IOException e) { 379 if (!file.canWrite()) 380 uif.showError("ce.save.cantWriteFile", file); 381 else if (e instanceof FileNotFoundException) 382 uif.showError("ce.save.cantFindFile", file); 383 else 384 uif.showError("ce.save.error", new Object[] { file, e } ); 385 } 386 catch (Interview.Fault e) { 387 uif.showError("ce.save.error", new Object[] { file, e.getMessage() } ); 388 } 389 390 return false; 391 } 392 393 /** 394 * Does actual save work. should be overriden, when needed. 395 */ 396 protected void doSave(File file) throws Interview.Fault, IOException { 397 viewConfig.save(file); 398 } 399 400 private File getSaveFile(File dir) { 401 FileChooser fileChooser = getFileChooser(); 402 fileChooser.setDialogTitle(uif.getI18NString("ce.save.title")); 403 return saveConfigFile(getContextManager(), parent, uif, fileChooser, dir, 404 this.templateMode); 405 } 406 407 private FileChooser getFileChooser() { 408 FileChooser fileChooser = new FileChooser(true); 409 fileChooser.addChoosableExtension(ext, uif.getI18NString("ce.jtiFiles")); 410 return fileChooser; 411 } 412 413 /** 414 * In viewConfig differs from mainConfig asks user whether save changes 415 * or not. Save changes in case of positive answer. 416 * @param que 417 * @return false iff user said "cancel". 418 */ 419 private boolean askAndSave(String question) { 420 if (isEdited()) { 421 int rc = uif.showYesNoCancelDialog(question); 422 423 switch (rc) { 424 case JOptionPane.YES_OPTION: { 425 save(); 426 return true;} 427 case JOptionPane.NO_OPTION: return true; 428 default: return false; 429 } 430 } else { 431 return true; 432 } 433 } 434 435 public void revert() { 436 if (!isEdited()) 437 return; 438 439 int rc = uif.showOKCancelDialog("ce.revert.warn"); 440 if (rc != JOptionPane.OK_OPTION) 441 return; 442 443 try { 444 copy(mainConfig, viewConfig); 445 if (currView != null && currView.isShowing()) 446 currView.load(); 447 updateTitle(); 448 } catch (Interview.Fault e) { 449 uif.showError("ce.revert", e.getMessage()); 450 } 451 } 452 453 public void setRunPending(boolean b) { 454 runPending = b; 455 } 456 457 public boolean isRunPending() { 458 return runPending; 459 } 460 461 public void show() { 462 if (stdView == null) 463 initGUI(); 464 465 show(DEFAULT_MODE); 466 } 467 468 public void updateMenu() { 469 FileHistory h = FileHistory.getFileHistory(viewConfig.getWorkDirectory(), getHistoryFileName()); 470 recentConfigMenu.setEnabled(h.getLatestEntry() != null); 471 } 472 473 public void show(int mode) { 474 if (stdView == null) { 475 initGUI(); 476 } 477 updateTitle(); 478 updateMenu(); 479 switch (mode) { 480 case DEFAULT_MODE: 481 show(getDefaultView()); 482 break; 483 484 case FULL_MODE: 485 show(fullView); 486 break; 487 488 case STD_MODE: 489 show(stdView); 490 break; 491 492 case STD_TESTS_MODE: 493 stdView.showTab(CE_StdView.TESTS_PANE); 494 show(stdView); 495 break; 496 497 case STD_EXCLUDE_LIST_MODE: 498 stdView.showTab(CE_StdView.EXCLUDE_LIST_PANE); 499 show(stdView); 500 break; 501 502 case STD_KEYWORDS_MODE: 503 stdView.showTab(CE_StdView.KEYWORDS_PANE); 504 show(stdView); 505 break; 506 507 case STD_KFL_MODE: 508 stdView.showTab(CE_StdView.KFL_PANE); 509 show(stdView); 510 break; 511 512 case STD_PRIOR_STATUS_MODE: 513 stdView.showTab(CE_StdView.PRIOR_STATUS_PANE); 514 show(stdView); 515 break; 516 517 case STD_ENVIRONMENT_MODE: 518 stdView.showTab(CE_StdView.ENVIRONMENT_PANE); 519 show(stdView); 520 break; 521 522 case STD_CONCURRENCY_MODE: 523 stdView.showTab(CE_StdView.CONCURRENCY_PANE); 524 show(stdView); 525 break; 526 527 case STD_TIMEOUT_FACTOR_MODE: 528 stdView.showTab(CE_StdView.TIMEOUT_FACTOR_PANE); 529 show(stdView); 530 break; 531 532 default: 533 throw new IllegalArgumentException(); 534 } 535 } 536 537 public void show(ActionListener closeListener) { 538 this.closeListener = closeListener; 539 show(); 540 } 541 542 public void show(int mode, ActionListener closeListener, boolean isTemplateMode) { 543 this.closeListener = closeListener; 544 show(mode); 545 } 546 547 public static final int DEFAULT_MODE = 0; 548 public static final int FULL_MODE = 1; 549 public static final int STD_MODE = 2; 550 public static final int STD_TESTS_MODE = 3; 551 public static final int STD_EXCLUDE_LIST_MODE = 4; 552 public static final int STD_KEYWORDS_MODE = 5; 553 public static final int STD_PRIOR_STATUS_MODE = 6; 554 public static final int STD_ENVIRONMENT_MODE = 7; 555 public static final int STD_CONCURRENCY_MODE = 8; 556 public static final int STD_TIMEOUT_FACTOR_MODE = 9; 557 public static final int TEMPLATE_FULL_MODE = 10; 558 public static final int STD_KFL_MODE = 11; 559 560 private void show(CE_View newView) { 561 562 // update viewConfig from currView (if showing) else mainConfig 563 if (currView != null && currView.isShowing()) { 564 currView.save(); 565 } 566 setRestorer(newView); 567 setView(newView); 568 setVisible(true); 569 } 570 571 @Override 572 public void setVisible(boolean isVisible) { 573 super.setVisible(isVisible); 574 notifyObserversOfVisibility(isVisible); 575 } 576 577 private void setView(CE_View newView) { 578 if (newView == null) 579 throw new NullPointerException(); 580 581 if (currView != null && currView == newView) { 582 currView.load(); 583 } 584 585 if (currView != newView) { 586 // note whether the focus is in the current view 587 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); 588 Component fo = kfm.getPermanentFocusOwner(); 589 boolean focusInView = (fo != null && currView != null && currView.isAncestorOf(fo)); 590 591 currView = newView; 592 593 // update currView from viewConfig 594 currView.load(); 595 596 // set up the appropriate view and controls 597 ((CardLayout)(views.getLayout())).show(views, currView.getName()); 598 599 // The following is a workaround for what may be a JDK bug. 600 // As a result of changing the view, the permanent focus owner may no longer 601 // be showing, and therefore not accepting any keyboard input. 602 if (focusInView) { 603 Container fcr = (currView.isFocusCycleRoot() ? currView : currView.getFocusCycleRootAncestor()); 604 FocusTraversalPolicy ftp = fcr.getFocusTraversalPolicy(); 605 Component c = ftp.getDefaultComponent(fcr); 606 if (c != null) 607 c.requestFocusInWindow(); 608 } 609 610 boolean currIsFull = (currView == fullView); 611 markerMenu.setEnabled(currIsFull); 612 searchMenu.setEnabled(currIsFull); 613 (currIsFull ? viewFullBtn : viewStdBtn).setSelected(true); 614 viewTagCheckBox.setEnabled(currIsFull); 615 616 if (detailsBrowser != null) 617 detailsBrowser.setQuestionInfoEnabled(currIsFull); 618 619 updateTitle(); 620 } 621 } 622 623 public void close() { 624 if (canInterruptTemplateCreation()) { 625 doClose(); 626 } else { 627 uif.showError("ce.force_close"); 628 } 629 } 630 631 public void doClose() { 632 if (currView != null && !currView.isOKToClose()) { 633 if (afterCloseCommand != null) { 634 afterCloseCommand.run(); 635 afterCloseCommand = null; 636 } 637 return; 638 } 639 640 close(true); 641 } 642 643 protected void windowClosingAction(AWTEvent e) { 644 if (!canInterruptTemplateCreation()) { 645 uif.showError("ce.force_close"); 646 return; 647 } 648 649 if(fullView.isVisible()) { 650 fullView.prepareClosing(); 651 } 652 doClose(); 653 } 654 655 656 private void close(boolean checkIfEdited) { 657 if (currView == null) 658 return; 659 660 if (!isShowing()) 661 return; 662 663 if (checkIfEdited && isEdited()) { 664 int rc = uif.showYesNoCancelDialog("ce.close.warn"); 665 switch (rc) { 666 case JOptionPane.YES_OPTION: 667 if (save0()) { 668 break; 669 } else { 670 if (afterCloseCommand != null) { 671 afterCloseCommand.run(); 672 afterCloseCommand = null; 673 } 674 return; 675 } 676 677 case JOptionPane.NO_OPTION: 678 break; 679 default: 680 if (afterCloseCommand != null) { 681 afterCloseCommand.run(); 682 afterCloseCommand = null; 683 } 684 return; 685 } 686 } 687 688 setVisible(false); 689 690 // closeListener may have been set by show(ActionListener) 691 if (closeListener != null) { 692 ActionEvent e = new ActionEvent(this, 693 ActionEvent.ACTION_PERFORMED, 694 CLOSE); 695 closeListener.actionPerformed(e); 696 closeListener = null; 697 } 698 699 if (afterCloseCommand != null) { 700 afterCloseCommand.run(); 701 afterCloseCommand = null; 702 } 703 704 } 705 706 public void setCheckExcludeListListener(ActionListener l) { 707 if (stdView == null) 708 initGUI(); 709 710 stdView.setCheckExcludeListListener(l); 711 } 712 713 boolean isCurrentQuestionChanged() { 714 if (currView != null && currView.isShowing()) 715 currView.save(); 716 717 Question mq = mainConfig.getCurrentQuestion(); 718 Question vq = viewConfig.getCurrentQuestion(); 719 return !equal(mq.getTag(), vq.getTag()); 720 } 721 722 723 boolean isEdited() { 724 if (currView != null && currView.isShowing()) 725 currView.save(); 726 727 return !equal(mainConfig, viewConfig); 728 } 729 730 /** 731 * Compares two InterviewParameters objects for equivalence. 732 * Two interview are equivalent when they both provide the same set 733 * of questions and all corresponding questions have the same values. 734 * 735 * @param a first interview 736 * @param b second interview 737 * @return true, iff two interviews are equivalent. 738 */ 739 public static boolean equal(InterviewParameters a, InterviewParameters b) { 740 if (a == b) { 741 return true; 742 } 743 744 if (a.isTemplate() != b.isTemplate()) { 745 return false; 746 } 747 // do ez checks first 748 if (a.getMarkersEnabled() != b.getMarkersEnabled() 749 || a.getMarkersFilterEnabled() != b.getMarkersFilterEnabled()) { 750 return false; 751 } 752 753 Map aQuestions = a.getAllQuestions(); 754 Map bQuestions = b.getAllQuestions(); 755 756 Set keys = new TreeSet(); 757 keys.addAll(aQuestions.keySet()); 758 keys.addAll(bQuestions.keySet()); 759 760 for (Iterator iter = keys.iterator(); iter.hasNext(); ) { 761 String key = (String) (iter.next()); 762 Question aq = (Question) aQuestions.get(key); 763 Question bq = (Question) bQuestions.get(key); 764 if (aq == null || bq == null) { 765 return false; 766 } 767 if (!aq.equals(bq)) { 768 String empty = ""; 769 boolean eq = (aq.getStringValue() == null && 770 empty.equals(bq.getStringValue())) || 771 (empty.equals(aq.getStringValue()) && 772 bq.getStringValue() == null); 773 if(!eq) { 774 /* 775 Hopefully, question reloading is not required anymore, 776 not because questions are equal now, but because 777 the unexpected dialog no longer appears... 778 779 // if aq is not set, it will be set to the default value 780 // (if the default value had been specified for aq before) 781 aq.reload(); 782 if (!aq.equals(bq)) { 783 return false; 784 } 785 */ 786 return false; 787 } 788 } 789 } 790 // Checking external values 791 Set aKeys = a.getPropertyKeys(); 792 Set bKeys = b.getPropertyKeys(); 793 794 if (aKeys == null || bKeys == null) { 795 return aKeys == bKeys; 796 } 797 798 if (aKeys.size() != bKeys.size()) { 799 return false; 800 } 801 802 for (Iterator iter = aKeys.iterator(); iter.hasNext(); ) { 803 String key = (String)iter.next(); 804 if (!bKeys.contains(key)) { 805 return false; 806 } 807 808 String aProp = a.retrieveProperty(key); 809 String bProp = b.retrieveProperty(key); 810 if (!equal(aProp, bProp)) { 811 return false; 812 } 813 } 814 815 816 return true; 817 } 818 819 /** 820 * Registers new observer 821 * @param o - observer to be added to the list 822 */ 823 public void addObserver(Observer o) { 824 if (o != null && !observers.contains(o)) { 825 observers.add(o); 826 } 827 } 828 /** 829 * Removes observer from the list 830 * @param o - observer to be removed from the list 831 */ 832 public void removeObserver(Observer o) { 833 if (o != null) { 834 observers.remove(o); 835 } 836 } 837 /** 838 * Notifies registered observers of the change happened to viewConfig 839 */ 840 protected void notifyObservers() { 841 for (Observer obs: observers) { 842 obs.changed(viewConfig); 843 } 844 } 845 /** 846 * Notifies registered observers of setVisible() method has been called. 847 */ 848 protected void notifyObserversOfVisibility(boolean isVisible) { 849 for (Observer obs: observers) { 850 obs.changedVisibility(isVisible, this); 851 } 852 } 853 854 private static boolean equal(String a, String b) { 855 return (a == null || b == null ? a == b : a.equals(b)); 856 } 857 858 @Override 859 public void dispose() { 860 if (viewConfig != null) { 861 viewConfig.dispose(); 862 viewConfig = null; 863 } 864 super.dispose(); 865 } 866 867 protected void initGUI() { 868 setHelp("confEdit.window.csh"); 869 listener = new Listener(); 870 871 updateTitle(); 872 873 if (viewConfig.getHelpSet() != null) { 874 // would prefer that the helpset came from the test suite 875 infoPanel = new JHelpContentViewer(Help.getHelpSet(viewConfig)); 876 infoPanel.setName("info"); 877 int dpi = uif.getDotsPerInch(); 878 infoPanel.setPreferredSize(new Dimension(4*dpi, 3*dpi)); 879 infoPanel.putClientProperty(HelpLink.HELPBROKER_FOR_HELPLINK, uif.getHelpBroker()); 880 } 881 882 fullView = new CE_FullView(viewConfig, infoPanel, uif, listener); 883 if (customRenderersMap != null) { 884 fullView.setCustomRenderers(customRenderersMap); 885 } 886 887 stdView = new CE_StdView(viewConfig, infoPanel, uif, listener); 888 stdView.setParentToolDialog(this); 889 890 initMenuBar(); 891 892 views = uif.createPanel("ce.views", new CardLayout(), false); 893 views.add(fullView, fullView.getName()); 894 views.add(stdView, stdView.getName()); 895 896 if (infoPanel == null) { 897 viewInfoCheckBox.setEnabled(false); 898 viewInfoCheckBox.setSelected(false); 899 } 900 else { 901 Preferences p = Preferences.access(); 902 boolean prefMoreInfo = p.getPreference(MORE_INFO_PREF, "true").equals("true"); 903 viewInfoCheckBox.setEnabled(true); 904 viewInfoCheckBox.setSelected(prefMoreInfo); 905 } 906 907 // set body to be views+info or views 908 if (viewInfoCheckBox.isSelected()) { 909 views.setBorder(null); 910 JSplitPane sp = uif.createSplitPane(JSplitPane.HORIZONTAL_SPLIT, views, infoPanel); 911 sp.setDividerLocation(views.getPreferredSize().width + sp.getDividerSize()); 912 body = sp; 913 } 914 else { 915 views.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 916 body = views; 917 } 918 919 // Don't register "shift alt D" on body, because body might change 920 // if the more info is opened/closed. 921 // Instead, register it on views and infoPanel 922 views.registerKeyboardAction(listener, DETAILS, detailsKey, 923 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 924 if (infoPanel != null) 925 infoPanel.registerKeyboardAction(listener, DETAILS, detailsKey, 926 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 927 928 setBody(body); 929 930 setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 931 } 932 933 protected JMenu createFileMenu() { 934 String[] fileMenuItems = new String[] { SAVE, SAVE_AS, REVERT, null, 935 NEW, LOAD, null, CLOSE }; 936 JMenu fileMenu = uif.createMenu("ce.file", fileMenuItems, listener); 937 938 FileHistory h = FileHistory.getFileHistory(viewConfig.getWorkDirectory(), getHistoryFileName()); 939 FileHistory.Listener l = new FileHistory.Listener(h, 0, (ActionListener)listener); 940 recentConfigMenu = uif.createMenu("ce.history"); 941 recentConfigMenu.setEnabled(h.getLatestEntry() != null); 942 recentConfigMenu.addMenuListener(l); 943 fileMenu.insert(recentConfigMenu, 4); 944 945 return fileMenu; 946 } 947 948 private void initMenuBar() { 949 listener = new Listener(); 950 JMenuBar menuBar = uif.createMenuBar("ce.menub"); 951 952 JMenu fileMenu = createFileMenu(); 953 menuBar.add(fileMenu); 954 955 // marker menu 956 markerMenu = fullView.getMarkerMenu(); 957 menuBar.add(markerMenu); 958 959 // search menu 960 searchMenu = fullView.getSearchMenu(); 961 menuBar.add(searchMenu); 962 963 // view menu 964 viewMenu = uif.createMenu("ce.view"); 965 viewMenu.addMenuListener(listener); 966 ButtonGroup viewGroup = new ButtonGroup(); 967 968 viewFullBtn = uif.createRadioButtonMenuItem("ce.view", CE_View.FULL); 969 viewFullBtn.setSelected(true); 970 viewFullBtn.setActionCommand(CE_View.FULL); 971 viewFullBtn.addActionListener(listener); 972 viewGroup.add(viewFullBtn); 973 viewMenu.add(viewFullBtn); 974 975 viewStdBtn = uif.createRadioButtonMenuItem("ce.view", CE_View.STD); 976 viewStdBtn.setActionCommand(CE_View.STD); 977 viewStdBtn.addActionListener(listener); 978 viewGroup.add(viewStdBtn); 979 viewMenu.add(viewStdBtn); 980 981 viewMenu.addSeparator(); 982 983 viewInfoCheckBox = uif.createCheckBoxMenuItem("ce.view", "info", false); 984 viewInfoCheckBox.addChangeListener(listener); 985 viewMenu.add(viewInfoCheckBox); 986 987 viewTagCheckBox = uif.createCheckBoxMenuItem("ce.view", "tag", false); 988 viewTagCheckBox.setAccelerator(KeyStroke.getKeyStroke("control T")); 989 viewTagCheckBox.addChangeListener(listener); 990 viewMenu.add(viewTagCheckBox); 991 992 viewMenu.addSeparator(); 993 994 viewRefreshItem = uif.createMenuItem("ce.view", "refresh", listener); 995 viewRefreshItem.setAccelerator(KeyStroke.getKeyStroke("F5")); 996 viewMenu.add(viewRefreshItem); 997 998 menuBar.add(viewMenu); 999 1000 menuBar.add(uif.createHorizontalGlue("ce.pad")); 1001 1002 // help menu 1003 JMenu helpMenu = uif.createMenu("ce.help"); 1004 // config editor help 1005 JMenuItem mainItem = uif.createHelpMenuItem("ce.help.main", "confEdit.window.csh"); 1006 helpMenu.add(mainItem); 1007 1008 /** 1009 // template editor help 1010 mainItem = uif.createHelpMenuItem("ce.help.maint", "confEdit.templateDialog.csh"); 1011 helpMenu.add(mainItem); 1012 */ 1013 JMenuItem fullItem = uif.createHelpMenuItem("ce.help.full", "confEdit.fullView.csh"); 1014 helpMenu.add(fullItem); 1015 JMenuItem stdItem = uif.createHelpMenuItem("ce.help.std", "confEdit.stdView.csh"); 1016 helpMenu.add(stdItem); 1017 menuBar.add(helpMenu); 1018 1019 setJMenuBar(menuBar); 1020 } 1021 1022 protected void updateTitle() { 1023 File f = viewConfig.getFile(); 1024 setI18NTitle("ce.title", 1025 new Object[] { new Integer(currView == fullView ? 0 : 1), 1026 new Integer(f == null ? 0 : 1), f }); 1027 } 1028 1029 private boolean isInfoVisible() { 1030 return (body instanceof JSplitPane); 1031 } 1032 1033 private void setInfoVisible(boolean b) { 1034 // verify there is an infoPanel to be made visible 1035 if (infoPanel == null) 1036 throw new IllegalStateException(); 1037 1038 // check if already set as desired 1039 if (b == isInfoVisible()) 1040 return; 1041 1042 // get dimensions of views and info panel 1043 Dimension viewsSize = views.getSize(); 1044 if (viewsSize.width == 0) 1045 viewsSize = views.getPreferredSize(); 1046 1047 Dimension infoSize = infoPanel.getSize(); 1048 if (infoSize.width == 0) 1049 infoSize = infoPanel.getPreferredSize(); 1050 1051 1052 if (b) { 1053 // set body to views+info; remove border, because JSplitPane adds in 1054 // its own padding 1055 views.setBorder(null); 1056 JSplitPane sp = uif.createSplitPane(JSplitPane.HORIZONTAL_SPLIT, views, infoPanel); 1057 sp.setDividerLocation(viewsSize.width + sp.getDividerSize()); 1058 body = sp; 1059 showInfoForQuestion(viewConfig.getCurrentQuestion()); 1060 } 1061 else { 1062 // set body to views; add a border to stand in for the padding 1063 // that JSplitPane would otherwise give 1064 views.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 1065 body = views; 1066 } 1067 1068 setBody(body); 1069 if (isShowing()) { 1070 // adjust the size of the window up or down as appropriate 1071 Dimension winSize = getSize(); 1072 int divWidth = new JSplitPane().getDividerSize(); 1073 int newWidth = winSize.width; 1074 newWidth += (b ? +1 : -1) * (infoSize.width + divWidth); 1075 setSize(newWidth, winSize.height); 1076 } 1077 } 1078 1079 private void showInfoForQuestion(Question q) { 1080 HelpID helpId = Help.getHelpID(q); 1081 // uugh 1082 if (helpId == null) 1083 System.err.println("WARNING: no help for " + q.getKey()); 1084 else 1085 infoPanel.setCurrentID(helpId); 1086 } 1087 1088 1089 private CE_View getDefaultView() { 1090 if (currView != null) 1091 return currView; 1092 Preferences p = Preferences.access(); 1093 String prefView = p.getPreference(VIEW_PREF, CE_View.FULL); 1094 if (prefView.equals(CE_View.STD)) 1095 return stdView; 1096 else 1097 return fullView; 1098 } 1099 1100 protected void perform(String cmd) { 1101 if (cmd.equals(NEW)) 1102 newConfigAsk(); 1103 else if (cmd.equals(LOAD)) 1104 loadConfigAsk(); 1105 /* 1106 else if (cmd.equals(LOADT)) 1107 load(true); 1108 else if (cmd.equals(NEWT)) 1109 clear(true); 1110 */ 1111 else if (cmd.equals(SAVE)) 1112 save(); 1113 else if (cmd.equals(SAVE_AS)) 1114 saveAs(); 1115 else if (cmd.equals(REVERT)) 1116 revert(); 1117 else if (cmd.equals(CE_View.FULL)) 1118 show(fullView); 1119 else if (cmd.equals(CE_View.STD)) 1120 show(stdView); 1121 else if (cmd.equals(CLOSE)) { 1122 close(); 1123 } 1124 else if (cmd.equals(DONE)) { 1125 if (currView != null && !currView.isOKToClose()) 1126 return; 1127 1128 if (!canInterruptTemplateCreation() && !viewConfig.isFinishable()) { 1129 uif.showError("ce.force_close"); 1130 return; 1131 } 1132 1133 currView.save(); 1134 if (!viewConfig.isFinishable()) { 1135 Integer rp = new Integer(runPending ? 1 : 0); 1136 int rc = uif.showOKCancelDialog("ce.okToClose", rp); 1137 if (rc != JOptionPane.OK_OPTION) 1138 return; 1139 } 1140 1141 if (isEdited() || isCurrentQuestionChanged()) 1142 saveRequired = true; 1143 1144 if (saveRequired) { 1145 if (!save0()) { 1146 // save failed, stay in CE, don't clear saveRequired flag 1147 return; 1148 } 1149 1150 // save succeeded, so safe to clear saveRequired flag 1151 saveRequired = false; 1152 } 1153 1154 close(false); 1155 } 1156 else if (cmd.equals(REFRESH)) { 1157 if (currView != null) 1158 currView.refresh(); 1159 } 1160 else if (cmd.equals(DETAILS)) { 1161 if (detailsBrowser == null) { 1162 detailsBrowser = new DetailsBrowser(body, viewConfig, infoPanel); 1163 detailsBrowser.setQuestionInfoEnabled(currView == fullView); 1164 } 1165 1166 detailsBrowser.setVisible(true); 1167 } 1168 else 1169 throw new IllegalArgumentException(cmd); 1170 } 1171 1172 private boolean canInterruptTemplateCreation () { 1173 /** fa 1174 ContextManager cm = getContextManager(); 1175 String wdTmpl = TemplateUtilities.getTemplatePath(model.getWorkDirectory()); 1176 if (mainConfig.isTemplate() && 1177 !cm.getFeatureManager().isEnabled(FeatureManager.WD_WITHOUT_TEMPLATE) && 1178 wdTmpl == null) { 1179 return false; 1180 } 1181 */ 1182 return true; 1183 } 1184 1185 public static void copy(InterviewParameters from, InterviewParameters to) 1186 throws Interview.Fault 1187 { 1188 copy(from, to, true); // copy filename as well, by default 1189 } 1190 1191 private static void copy(InterviewParameters from, InterviewParameters to, 1192 boolean copyFile) 1193 throws Interview.Fault 1194 { 1195 //System.err.println("CE.copy from " + (from==mainConfig?"main":from==viewConfig?"view":from.toString()) + " to " + (to==mainConfig?"main":to==viewConfig?"view":to.toString())); 1196 HashMap data = new HashMap(); 1197 from.save(data); 1198 to.load(data, false); 1199 to.setTemplate(from.isTemplate()); 1200 1201 if (copyFile) 1202 to.setFile(from.getFile()); 1203 if (debug) { 1204 Debug.println("InterviewEditor: equal(b,a) " + equal(to,from)); 1205 } 1206 1207 } 1208 1209 /** 1210 * Checks default settings relate to config file load fron the default location 1211 * @param cm <code>ContextManager</code> object defining current harness' context. The following methods 1212 * affect this method functionality: 1213 * <ul> 1214 * <li><code>getDefaultConfigLoadPath()</code> 1215 * <li><code>getAllowConfigLoadOutsideDefault()</code> 1216 * </ul> 1217 * @throws <code>IllegalArgumentException</code> if the following configuration errors found: 1218 * <ul> 1219 * <li> <code>getDefaultConfigLoadPath()</code> returns <code>null</code> when <code>getAllowConfigLoadOutsideDefault()</code> returns <code>false</code> 1220 * <li> <code>getDefaultConfigLoadPath()</code> returns not absolute path 1221 * <li> <code>getDefaultConfigLoadPath()</code> returns a file (not a directory) 1222 * </ul> 1223 * @see ContextManager#setDefaultConfigLoadPath(java.io.File) 1224 * @see ContextManager#setAllowConfigLoadOutsideDefault(boolean state) 1225 * @see ContextManager#getDefaultConfigLoadPath() 1226 * @see ContextManager#getAllowConfigLoadOutsideDefault() 1227 */ 1228 1229 public static File checkLoadConfigFileDefaults(ContextManager cm) { 1230 if (cm == null) 1231 return null; 1232 1233 File defaultConfigLoadPath = cm.getDefaultConfigLoadPath(); 1234 boolean allowConfigLoadOutsideDefault = cm.getAllowConfigLoadOutsideDefault(); 1235 1236 if (defaultConfigLoadPath == null && !allowConfigLoadOutsideDefault) 1237 throw new IllegalArgumentException("Default directory not specified for " + 1238 "load operation when allowConfigLoadOutsideDefault is false"); 1239 1240 if (defaultConfigLoadPath != null) { 1241 if (!defaultConfigLoadPath.isAbsolute()) 1242 throw new IllegalArgumentException("Relative paths not " + 1243 "currently supported. The following setting is incorrect: " + 1244 "\"" + defaultConfigLoadPath.getPath() + "\" selected for " + 1245 "load operation"); 1246 1247 if (defaultConfigLoadPath.isFile()) 1248 throw new IllegalArgumentException("Filename selected unexpectedly " + 1249 "as a default directory: " + 1250 "\"" + defaultConfigLoadPath.getPath() + "\" for " + 1251 "load operation"); 1252 } 1253 1254 return defaultConfigLoadPath; 1255 } 1256 1257 static File loadConfigFile(ContextManager cm, Component parent, UIFactory uif, String ext, String key) { 1258 FileChooser fileChooser = new FileChooser(true); 1259 fileChooser.addChoosableExtension(ext, uif.getI18NString("ce.jtiFiles" + key)); 1260 return loadConfigFile(cm, parent, uif, fileChooser); 1261 } 1262 1263 /** 1264 * Provides capabilities for configuration file loading. Method takes into 1265 * account context settings relating to default locations for configuration 1266 * files loading and behaves according to them. 1267 * @param cm <code>ContextManager</code> object defining current harness' context. The following methods 1268 * affect this method functionality: 1269 * <li><code>getDefaultConfigLoadPath()</code> 1270 * <li><code>getAllowConfigLoadOutsideDefault()</code> 1271 * </ul> 1272 * @param parent A parent frame to be used for <code>fileChooser</code>/warning dialogs 1273 * @param uif The UIFactory used to for configuration file loading operation 1274 * @param fileChooser The <code>FileChooser</code> used for configuration file loading 1275 * @return The configuration file selected by user if this file loading is allowed by 1276 * harness' contest settings 1277 * @see ContextManager#setDefaultConfigLoadPath(java.io.File) 1278 * @see ContextManager#setAllowConfigLoadOutsideDefault(boolean state) 1279 * @see ContextManager#getDefaultConfigLoadPath() 1280 * @see ContextManager#getAllowConfigLoadOutsideDefault() 1281 */ 1282 1283 static File loadConfigFile(ContextManager cm, Component parent, UIFactory uif, FileChooser fileChooser) { 1284 1285 if (cm == null) 1286 return null; 1287 1288 File defaultConfigLoadPath = checkLoadConfigFileDefaults(cm); 1289 boolean allowConfigLoadOutsideDefault = cm.getAllowConfigLoadOutsideDefault(); 1290 1291 File file = null; 1292 1293 fileChooser.setDialogTitle(uif.getI18NString("ce.load.title")); 1294 1295 if (defaultConfigLoadPath != null) { 1296 if (!allowConfigLoadOutsideDefault) { 1297 if (!(new File(defaultConfigLoadPath.getAbsolutePath())).canRead()) { 1298 uif.showError("ce.load.defDirNotExists", defaultConfigLoadPath); 1299 return null; 1300 } 1301 fileChooser.enableDirectories(false); 1302 } else 1303 fileChooser.enableDirectories(true); 1304 fileChooser.setCurrentDirectory(defaultConfigLoadPath); 1305 } 1306 1307 boolean isMatch = true; 1308 1309 while (file == null) { 1310 int rc = fileChooser.showDialog(parent, uif.getI18NString("ce.load.btn")); 1311 if (rc != JFileChooser.APPROVE_OPTION) 1312 return null; 1313 1314 file = fileChooser.getSelectedFile(); 1315 1316 if (!allowConfigLoadOutsideDefault) { 1317 if (defaultConfigLoadPath == null) 1318 return null; 1319 1320 File f = new File(file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf(File.separator))); 1321 1322 try { 1323 isMatch = (f.getCanonicalPath().indexOf((defaultConfigLoadPath.getCanonicalPath())) == 0); 1324 } catch ( IOException ioe) { 1325 ioe.printStackTrace(System.err); 1326 return null; 1327 } 1328 1329 if (!isMatch) { 1330 uif.showError("ce.load.notAllowedDir", defaultConfigLoadPath); 1331 1332 1333 file = null; 1334 fileChooser.setCurrentDirectory(defaultConfigLoadPath); 1335 continue; // choose another file 1336 } 1337 } 1338 } 1339 1340 if (file != null) { 1341 String path = file.getPath(); 1342 String ext = fileChooser.getChosenExtension(); 1343 if (ext == null) { 1344 ext = CONFIG_EXTENSION; 1345 } 1346 if (!path.endsWith(ext)) 1347 file = new File(path + ext); 1348 } 1349 1350 return file; 1351 } 1352 1353 /** 1354 * Provides as the user with a dialog to chooser where to save a config. Method takes into account 1355 * context settings relating to default locations for configuration files saving and behaves 1356 * according to them. 1357 * @param cm <code>ContextManager</code> object defining current harness' context. The following methods 1358 * affect this method functionality: 1359 * <ul> 1360 * <li><code>getDefaultConfigSavePath()</code> 1361 * <li><code>getAllowConfigSaveOutsideDefault()</code> 1362 * </ul> 1363 * @param parent A parent frame to be used for <code>fileChooser</code>/warning dialogs 1364 * @param uif The UIFactory used to for configuration file saving operation 1365 * @param fileChooser The <code>FileChooser</code> used for configuration file saving 1366 * @return The configuration file selected by user if this file saving is allowed by 1367 * harness' contest settings 1368 * @throws <code>IllegalArgumentException</code> if the following configuration errors found: 1369 * <ul> 1370 * <li> <code>getDefaultConfigSavePath()</code> returns <code>null</code> when <code>getAllowConfigSaveOutsideDefault()</code> returns <code>false</code> 1371 * <li> <code>getDefaultConfigSavePath()</code> returns not absolute path 1372 * <li> <code>getDefaultConfigSavePath()</code> returns a file (not a directory) 1373 * </ul> 1374 * @see ContextManager#setDefaultConfigSavePath(java.io.File) 1375 * @see ContextManager#setAllowConfigSaveOutsideDefault(boolean state) 1376 * @see ContextManager#getDefaultConfigSavePath() 1377 * @see ContextManager#getAllowConfigSaveOutsideDefault() 1378 */ 1379 1380 static File saveConfigFile(ContextManager cm, Component parent, UIFactory uif, FileChooser fileChooser, File dir, 1381 boolean isTemplate) { 1382 if (cm == null) 1383 return null; 1384 1385 File defaultSavePath; 1386 if (isTemplate) { 1387 defaultSavePath = cm.getDefaultTemplateSavePath(); 1388 } else { 1389 defaultSavePath = cm.getDefaultConfigSavePath(); 1390 } 1391 boolean allowSaveOutsideDefault; 1392 if (isTemplate) { 1393 allowSaveOutsideDefault = cm.getAllowTemplateSaveOutsideDefault(); 1394 } else { 1395 allowSaveOutsideDefault = cm.getAllowConfigSaveOutsideDefault(); 1396 } 1397 1398 1399 if (defaultSavePath == null && !allowSaveOutsideDefault) 1400 throw new IllegalArgumentException("Default directory not specified for " + 1401 "save operation when allowConfigSaveOutsideDefault is false"); 1402 1403 if (defaultSavePath != null) { 1404 if (!defaultSavePath.isAbsolute()) 1405 throw new IllegalArgumentException("Relative paths not " + 1406 "currently supported. The following setting is incorrect: " + 1407 "\"" + defaultSavePath.getPath() + "\" selected for " + 1408 "save operation"); 1409 1410 if (defaultSavePath.isFile()) 1411 throw new IllegalArgumentException("Filename selected unexpectedly " + 1412 "as a default directory: " + 1413 "\"" + defaultSavePath.getPath() + "\" for " + 1414 "save operation"); 1415 1416 if (!allowSaveOutsideDefault) { 1417 if (!defaultSavePath.canWrite()) { 1418 uif.showError("ce.save.defDirNotExists", defaultSavePath); 1419 return null; 1420 } 1421 fileChooser.enableDirectories(false); 1422 } else 1423 fileChooser.enableDirectories(true); 1424 1425 fileChooser.setCurrentDirectory(defaultSavePath); 1426 } else 1427 if (dir != null) 1428 fileChooser.setCurrentDirectory(dir); 1429 1430 File file = null; 1431 boolean isMatch = true; 1432 1433 while (file == null) { 1434 int rc = fileChooser.showDialog(parent, uif.getI18NString("ce.save.btn")); 1435 if (rc != JFileChooser.APPROVE_OPTION) 1436 // user has canceled or closed the chooser 1437 return null; 1438 1439 file = fileChooser.getSelectedFile(); 1440 if (file == null) // just making sure 1441 continue; 1442 1443 File f = new File(file.getAbsolutePath().substring(0, file.getAbsolutePath().lastIndexOf(File.separator))); 1444 1445 if (!allowSaveOutsideDefault) { 1446 if (defaultSavePath == null) 1447 return null; 1448 1449 try { 1450 isMatch = defaultSavePath.getCanonicalPath().equals(f.getCanonicalPath()); 1451 } catch ( IOException ioe) { 1452 ioe.printStackTrace(System.err); 1453 return null; 1454 } 1455 1456 if (!isMatch) { 1457 uif.showError("ce.save.notAllowedDir", defaultSavePath); 1458 file = null; 1459 fileChooser.setCurrentDirectory(defaultSavePath); 1460 continue; // choose another file 1461 } 1462 } 1463 1464 if (file.isDirectory()) { 1465 uif.showError("ce.save.fileIsDir", file); 1466 file = null; 1467 continue; // choose another file 1468 } 1469 1470 File parentFile = file.getParentFile(); 1471 if (parentFile != null) { 1472 if (parentFile.exists() && !parentFile.isDirectory()) { 1473 uif.showError("ce.save.parentNotADir", parentFile); 1474 file = null; 1475 continue; // choose another file 1476 } else if (!parentFile.exists()) { 1477 rc = uif.showYesNoDialog("ce.save.createParentDir", 1478 parentFile); 1479 if (rc == JOptionPane.YES_OPTION) { 1480 if (!parentFile.mkdirs()) { 1481 uif.showError("ce.save.cantCreateParentDir", 1482 parentFile); 1483 file = null; 1484 continue; // choose another file 1485 } 1486 } else { 1487 file = null; 1488 continue; // choose another file 1489 } 1490 } 1491 } 1492 1493 // if file exists, leave well enough alone; 1494 // otherwise, make sure it ends with .jti or .jtm 1495 if (!file.exists()) { 1496 String path = file.getPath(); 1497 String ext = fileChooser.getChosenExtension(); 1498 if (ext != null && !path.endsWith(ext)) 1499 file = new File(path + ext); 1500 } 1501 1502 // if file exists, make sure user wants to overwrite it 1503 if (file.exists()) { 1504 rc = uif.showYesNoDialog("ce.save.warn"); 1505 switch (rc) { 1506 case JOptionPane.YES_OPTION: 1507 break; // use this file 1508 1509 case JOptionPane.NO_OPTION: 1510 fileChooser.setSelectedFile(null); 1511 file = null; 1512 continue; // choose another file 1513 } 1514 } 1515 } 1516 return file; 1517 } 1518 1519 void setAfterCloseCommand(Runnable runnable) { 1520 afterCloseCommand = runnable; 1521 } 1522 1523 private Runnable afterCloseCommand; 1524 1525 /** 1526 * Will be eliminated in the next release. 1527 * @deprecated 1528 */ 1529 @Deprecated 1530 protected boolean templateMode = false; 1531 protected ContextManager contextManager = null; 1532 protected InterviewParameters mainConfig; 1533 protected InterviewParameters viewConfig; 1534 private FileHistory history; 1535 private boolean saveRequired; 1536 //protected String key; 1537 1538 private boolean runPending; 1539 private static boolean debug = Debug.getBoolean(InterviewEditor.class); 1540 1541 1542 1543 private JMenu recentConfigMenu; 1544 private JMenu markerMenu; 1545 private JMenu searchMenu; 1546 private JMenu viewMenu; 1547 private JRadioButtonMenuItem viewFullBtn; 1548 private JRadioButtonMenuItem viewStdBtn; 1549 private JCheckBoxMenuItem viewInfoCheckBox; 1550 private JCheckBoxMenuItem viewTagCheckBox; 1551 private JMenuItem viewRefreshItem; 1552 private JComponent body; 1553 private JPanel views; 1554 private JHelpContentViewer infoPanel; 1555 private CE_FullView fullView; 1556 private CE_StdView stdView; 1557 private CE_View currView; 1558 private Listener listener; 1559 //private TemplatesUI templatesUI; 1560 1561 private Map customRenderersMap; 1562 private ActionListener closeListener; 1563 //private ExecModel model; 1564 private final List<Observer> observers = new ArrayList<Observer>(); 1565 1566 private DetailsBrowser detailsBrowser; 1567 private static final KeyStroke detailsKey = KeyStroke.getKeyStroke("shift alt D"); 1568 protected String ext; 1569 1570 // XXX this isn't the right class to define these in 1571 // do not make more public than package private 1572 static final String CONFIG_EXTENSION = ".jti"; 1573 static final String CONFIG_HISTORY = "configHistory.jtl"; 1574 1575 private static final String NEW = "new"; 1576 private static final String LOAD = "load"; 1577 //private static final String NEWT = "newt"; 1578 //private static final String LOADT = "loadt"; 1579 private static final String SAVE = "save"; 1580 private static final String SAVE_AS = "saveAs"; 1581 private static final String REVERT = "revert"; 1582 private static final String DONE = "done"; 1583 private static final String REFRESH = "refresh"; 1584 private static final String DETAILS = "details"; 1585 static final String CLOSE = "close"; 1586 1587 static final String MORE_INFO_PREF = "exec.config.moreInfo"; 1588 static final String VIEW_PREF = "exec.config.view"; 1589 1590 1591 public void setCustomRenderers(Map renderersMap) { 1592 customRenderersMap = renderersMap; 1593 if (fullView != null) { 1594 fullView.setCustomRenderers(customRenderersMap); 1595 } 1596 } 1597 1598 private class Listener 1599 implements ActionListener, ChangeListener, MenuListener 1600 { 1601 // ---------- from ActionListener ----------- 1602 1603 public void actionPerformed(ActionEvent e) { 1604 Object src = e.getSource(); 1605 if (src instanceof JMenuItem) { 1606 JMenuItem mi = (JMenuItem) src; 1607 File f = (File) (mi.getClientProperty(FileHistory.FILE)); 1608 if (f != null && askAndSave("ce.load.warn")) { 1609 loadConfigFromFile(f); 1610 return; 1611 } 1612 } 1613 perform(e.getActionCommand()); 1614 } 1615 1616 // ---------- from ChangeListener ----------- 1617 1618 public void stateChanged(ChangeEvent e) { 1619 Object src = e.getSource(); 1620 if (src == viewInfoCheckBox && infoPanel != null) 1621 setInfoVisible(viewInfoCheckBox.isSelected()); 1622 else if (src == viewTagCheckBox) 1623 fullView.setTagVisible(viewTagCheckBox.isSelected()); 1624 } 1625 1626 // ---------- from MenuListener ----------- 1627 1628 public void menuSelected(MenuEvent e) { 1629 Object src = e.getSource(); 1630 if (src == viewMenu) 1631 viewTagCheckBox.setSelected(fullView.isTagVisible()); 1632 } 1633 1634 public void menuDeselected(MenuEvent e) { 1635 } 1636 1637 public void menuCanceled(MenuEvent e) { 1638 } 1639 1640 }; 1641 1642 /** 1643 * For private communication with SessionControl, not for broadcast outside 1644 * of core JT. 1645 */ 1646 public interface Observer { 1647 /** 1648 * Invoked when value of interview parameters has been changed 1649 * @param p object with updated value (viewConfig) 1650 */ 1651 public void changed(InterviewParameters p); 1652 1653 /** 1654 * Invoked when setVisible() method is invoked on InterviewEditor object 1655 * @param isVisible argument passed to setVisible() method 1656 * @param source editor that changed the state 1657 */ 1658 public void changedVisibility(boolean isVisible, InterviewEditor source); 1659 } 1660 }