1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2002, 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.javatest.InterviewParameters;
  30 import com.sun.javatest.TestFilter;
  31 import com.sun.javatest.report.CustomReport.ReportConfigPanel;
  32 import com.sun.javatest.report.CustomReport;
  33 import com.sun.javatest.report.Report;
  34 import com.sun.javatest.report.ReportDirChooser;
  35 import com.sun.javatest.Status;
  36 import com.sun.javatest.report.Report.StartGenListener;
  37 import com.sun.javatest.report.ReportSettings;
  38 import com.sun.javatest.tool.ToolDialog;
  39 import com.sun.javatest.tool.UIFactory;
  40 import com.sun.javatest.util.DynamicArray;
  41 
  42 import java.awt.BorderLayout;
  43 import java.awt.CardLayout;
  44 import java.awt.Component;
  45 import java.awt.Container;
  46 import java.awt.Dimension;
  47 import java.awt.Font;
  48 import java.awt.event.ActionEvent;
  49 import java.awt.event.ActionListener;
  50 import java.awt.event.InputEvent;
  51 import java.awt.event.KeyEvent;
  52 import java.awt.event.KeyListener;
  53 import java.awt.event.MouseEvent;
  54 import java.awt.EventQueue;
  55 import java.awt.GridBagConstraints;
  56 import java.awt.GridBagLayout;
  57 import java.beans.PropertyChangeEvent;
  58 import java.beans.PropertyChangeListener;
  59 import java.io.File;
  60 import java.io.IOException;
  61 import java.util.ArrayList;
  62 import java.util.Collection;
  63 import java.util.HashMap;
  64 import java.util.HashSet;
  65 import java.util.Iterator;
  66 import java.util.Map;
  67 
  68 import javax.swing.AbstractAction;
  69 import javax.swing.Action;
  70 import javax.swing.BorderFactory;
  71 import javax.swing.DefaultListModel;
  72 import javax.swing.event.ChangeEvent;
  73 import javax.swing.event.ChangeListener;
  74 import javax.swing.event.ListSelectionEvent;
  75 import javax.swing.event.ListSelectionListener;
  76 import javax.swing.event.MouseInputAdapter;
  77 import javax.swing.JButton;
  78 import javax.swing.JCheckBox;
  79 import javax.swing.JComponent;
  80 import javax.swing.JDialog;
  81 import javax.swing.JFileChooser;
  82 import javax.swing.JLabel;
  83 import javax.swing.JList;
  84 import javax.swing.JOptionPane;
  85 import javax.swing.JPanel;
  86 import javax.swing.JScrollPane;
  87 import javax.swing.JTabbedPane;
  88 import javax.swing.JTextArea;
  89 import javax.swing.JTextField;
  90 import javax.swing.KeyStroke;
  91 import javax.swing.ListCellRenderer;
  92 import javax.swing.ListModel;
  93 import javax.swing.ListSelectionModel;
  94 import javax.swing.ScrollPaneConstants;
  95 import javax.swing.SwingConstants;
  96 import javax.swing.text.Keymap;
  97 import javax.swing.Timer;
  98 import javax.swing.text.JTextComponent;
  99 
 100 class NewReportDialog extends ToolDialog
 101 {
 102 
 103     //----------------------------------------------------------------------------
 104 
 105     private static final String ACTION_MAP_KEY = "listMnemonics";
 106 
 107     interface Observer {
 108         // if ever made public, should rethink method names and if any helpful
 109         // parameters are passed
 110 
 111        /**
 112          * The dialog is done. Notify the observer to keep a snapshot of
 113          * current state.
 114          */
 115         public void update(Map<String, String> l);
 116 
 117         // starting
 118         public void writingReport();
 119 
 120         // done
 121         public void wroteReport();
 122 
 123         // error creating report
 124         // upgrade to use resource bundle+key as message
 125         // the given error occurred, and the user was informed
 126         public void errorWriting(String problem);
 127     }
 128 
 129     NewReportDialog(Component parent, UIFactory uif, FilterConfig f,
 130                     ReportBrowser reportBrowser, ExecModel model) {
 131         super(parent, uif, "nrd");
 132 
 133         this.model = model;
 134         filterHandler = f.createFilterSelectionHandler();
 135         this.reportBrowser = reportBrowser;
 136     }
 137 
 138     public void setInterviewParameters(InterviewParameters p) {
 139          interviewParams = p;
 140     }
 141 
 142     // ---------------------------------------------------------------------------
 143 
 144     void setLastState(Map h) {
 145         String rd = (String) (h.get(REPORT_DIR));
 146         String filter = (String) (h.get(FILTER));
 147 
 148         if (dirField == null)
 149             initGUI();
 150 
 151         dirField.setText(rd);
 152 
 153         if (filter != null && filter.length() > 0)
 154            filterHandler.setFilter(filter);
 155 
 156         updateHtmlCheckboxStates();
 157         updateCofCheckboxStates();
 158     }
 159 
 160     Map<String, String> getLastState() {
 161         String rd = dirField.getText();
 162         String filter = filterHandler.getActiveFilter().getName();
 163         Map<String, String> lastState = new HashMap<>();
 164 
 165         if (rd != null && rd.length() > 0)
 166            lastState.put(REPORT_DIR, rd);
 167 
 168         if (filter != null && filter.length() > 0)
 169            lastState.put(FILTER, filter);
 170 
 171         return lastState;
 172     }
 173 
 174     void addObserver(Observer o) {
 175         obs = (Observer[])DynamicArray.append(obs, o);
 176     }
 177 
 178     void removeObserver(Observer o) {
 179         obs = (Observer[])DynamicArray.remove(obs, o);
 180     }
 181 
 182     // Notify the observers that a change has been made.
 183     private void notifyUpdate(Map<String, String> s) {
 184         for (Observer o : obs) {
 185             o.update(s);
 186         }
 187     }
 188 
 189     private void notifyStarting() {
 190         for (Observer o : obs) {
 191             o.writingReport();
 192         }
 193     }
 194 
 195     private void notifyDone() {
 196         for (Observer o : obs) {
 197             o.wroteReport();
 198         }
 199     }
 200 
 201     private void notifyError(String problem) {
 202         for (Observer o : obs) {
 203             o.wroteReport();
 204         }
 205     }
 206 
 207     private void notifyErrorWriting(String problem) {
 208         for (Observer o : obs) {
 209             o.errorWriting(problem);
 210         }
 211     }
 212 
 213     // ---------------------------------------------------------------------------
 214 
 215     protected void initGUI() {
 216         setHelp("report.newReport.csh");
 217         setI18NTitle("nrd.title");
 218 
 219         JPanel body = new JPanel() {
 220                 public Dimension getPreferredSize() {
 221                     Dimension d = super.getPreferredSize();
 222                     int dpi = uif.getDotsPerInch();
 223                     return new Dimension(Math.max(d.width, 5 * dpi), d.height);
 224                 }
 225             };
 226         body.setName("nrd.body");
 227         body.setFocusable(false);
 228         body.setLayout(new GridBagLayout());
 229         body.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
 230 
 231         int dpi = uif.getDotsPerInch();
 232 
 233         GridBagConstraints lc = new GridBagConstraints();
 234         lc.anchor = GridBagConstraints.EAST;
 235         lc.insets.right = 10;
 236         lc.insets.bottom = 11;
 237         lc.weighty = 0.0;
 238         lc.weightx = 0.0;
 239         lc.fill = GridBagConstraints.HORIZONTAL;
 240         lc.gridwidth = 1;
 241 
 242         // first row, directory name
 243         JLabel dirLabel = uif.createLabel("nrd.dir", true);
 244         body.add(dirLabel, lc);
 245 
 246         dirField = uif.createInputField("nrd.dir.choice", dirLabel);
 247         lc.weightx = 3.0;
 248         body.add(dirField, lc);
 249 
 250         browseBtn = uif.createButton("nrd.browse", listener);
 251         browseBtn.setBorder(BorderFactory.createCompoundBorder(
 252             BorderFactory.createEtchedBorder(),
 253             BorderFactory.createEmptyBorder(0,3,0,3)));
 254         lc.gridwidth = GridBagConstraints.REMAINDER;
 255         lc.insets.left = 11;
 256         lc.weightx = 0.0;
 257         body.add(browseBtn, lc);
 258 
 259         // second row, filter
 260         lc.insets.left = 0;
 261         lc.gridwidth = 1;
 262 
 263         // add filter selector
 264         if (filterHandler != null) {
 265             JLabel filterLabel = uif.createLabel("nrd.filter", true);
 266             body.add(filterLabel, lc);
 267 
 268             JComponent selector = filterHandler.getFilterSelector();
 269             filterLabel.setLabelFor(selector);
 270             lc.gridwidth = GridBagConstraints.REMAINDER;
 271             lc.weightx = 4.0;
 272             lc.anchor = GridBagConstraints.WEST;
 273             lc.fill = GridBagConstraints.NONE;
 274             body.add(selector, lc);
 275         }
 276 
 277         // third row, the tabs
 278         lc.anchor = GridBagConstraints.NORTHWEST;
 279         lc.gridwidth = GridBagConstraints.REMAINDER;
 280         lc.fill = GridBagConstraints.BOTH;
 281         lc.insets.bottom = 12;
 282         lc.weighty = 1.0;
 283 
 284 //------------------------------------------------------------------------
 285 
 286         JPanel middle = uif.createPanel("nrd.middle", false);
 287         middle.setLayout(new BorderLayout());
 288         middle.setBorder(BorderFactory.createCompoundBorder(
 289                         uif.createTitledBorder("nrd.middle"),
 290                         BorderFactory.createEmptyBorder(12,12,12,12)));
 291 
 292         panes = new JComponent[] {
 293             createHtmlBlock(),
 294             createFilesBlock(),
 295             createKflBlock()
 296         };
 297 
 298         tabs = uif.createTabbedPane("nrd.tabs", panes);
 299         tabs.setTabPlacement(SwingConstants.TOP);
 300         tabs.setBorder(BorderFactory.createEmptyBorder());
 301 
 302         listModel = new DefaultListModel<>();
 303 
 304         // populate list and card panel
 305         final CardLayout cards = new CardLayout();
 306         final JPanel p = uif.createPanel("nrd.typecards", cards, false);
 307 
 308         cbHtml = uif.createCheckBox("nrd.type.html", true);
 309         //cbHtml.addActionListener(cbListener);
 310         listModel.addElement(cbHtml);
 311         p.add("nrd.type.html", tabs);
 312 
 313         cbPlain = uif.createCheckBox("nrd.type.pt", true);
 314         listModel.addElement(cbPlain);
 315         p.add("nrd.type.pt", uif.createPanel("nrd.blank", false));
 316 
 317         cbXml = uif.createCheckBox("nrd.type.xml", false);
 318         listModel.addElement(cbXml);
 319         p.add("nrd.type.xml", uif.createPanel("nrd.blank", false));
 320 
 321         cbCof = uif.createCheckBox("nrd.type.cof", false);
 322         listModel.addElement(cbCof);
 323 
 324         JPanel cofpanel = createCofPane();
 325         p.add("nrd.type.cof", cofpanel);
 326 
 327         getCustomReports(p);
 328 
 329         list = uif.createList("nrd.typel", listModel);
 330         list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
 331         SelectListener sl = new SelectListener(list, p, cards);
 332         list.addMouseListener(sl);
 333         list.addKeyListener(sl);
 334         list.addListSelectionListener(sl);
 335         list.setCellRenderer(new CheckBoxListCellRenderer());
 336         list.setBorder(BorderFactory.createCompoundBorder(
 337             BorderFactory.createEtchedBorder(),
 338             BorderFactory.createEmptyBorder(0,3,0,3)));
 339 
 340         infoArea = uif.createMessageArea("nrd.info");
 341         infoArea.setRows(3);
 342         Font f = infoArea.getFont();
 343         f = f.deriveFont(Font.PLAIN, f.getSize2D()-1);
 344         infoArea.setFont(f);
 345 
 346         // create container for right side (next to list)
 347         JPanel right = uif.createPanel("nrd.rptright", false);
 348         right.setLayout(new BorderLayout());
 349         right.add(infoArea, BorderLayout.PAGE_START);
 350         right.add(p, BorderLayout.CENTER);
 351         middle.add(right, BorderLayout.CENTER);
 352 
 353         JScrollPane listPane = uif.createScrollPane(list);
 354 
 355         middle.add(listPane, BorderLayout.WEST);
 356         body.add(middle, lc);
 357 //-----------------------------------------------------------------
 358 
 359         Action listMnemonics = new AbstractAction() {
 360 
 361                         public void actionPerformed(ActionEvent e) {
 362                                 int ord;
 363                 try {
 364                     ord = Integer.parseInt(e.getActionCommand()) - 1;
 365                 } catch (Exception ex) {
 366                     // ignore
 367                     return;
 368                 }
 369                                 if (ord == -1) ord = 9;
 370                                 if (ord <= list.getModel().getSize()) {
 371                                         list.requestFocusInWindow();
 372                                         list.setSelectedIndex(ord);
 373                                 }
 374                         }
 375 
 376         };
 377 
 378         list.getActionMap().put(ACTION_MAP_KEY, listMnemonics);
 379 
 380         int itemCount = listModel.size();
 381         if (itemCount > 9) itemCount = 9;
 382         for (int i = 1; i <= itemCount; ++i) {
 383                 list.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(i + '0', InputEvent.ALT_DOWN_MASK), ACTION_MAP_KEY);
 384                 JCheckBox box = listModel.getElementAt(i - 1);
 385                 box.setMnemonic(i + '0');
 386                 box.setText(Character.toString((char)('0' + i)) + " " + box.getText());
 387         }
 388 
 389         if (listModel.size() == 10){
 390                 list.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('0', InputEvent.ALT_DOWN_MASK), ACTION_MAP_KEY);
 391                 JCheckBox box = listModel.getElementAt(9);
 392                 box.setMnemonic('0');
 393                 box.setText("0 " + box.getText());
 394         }
 395 
 396 //-----------------------------------------------------------------
 397         JPanel bottom = createBackupOpsPane();
 398         bottom.setBorder(BorderFactory.createCompoundBorder(
 399                         uif.createTitledBorder("nrd.backup"),
 400                         BorderFactory.createEmptyBorder(12,12,12,12)));
 401 
 402         body.add(bottom, lc);
 403         setBody(body);
 404 //--------------------------------------------------------------------------------------
 405 
 406         okBtn = uif.createButton("nrd.ok", listener);
 407         sl.setOkBtn(okBtn);
 408         cancelBtn = uif.createCancelButton("nrd.cancel", listener);
 409         JButton helpBtn = uif.createHelpButton("nrd.help","report.newReport.csh" );
 410         setButtons(new JButton[] { okBtn, cancelBtn, helpBtn }, cancelBtn);
 411 
 412         list.setSelectedIndex(0);
 413         setState(Report.getSettingsPrefs());
 414     }
 415 
 416     /**
 417      * Options for the HTML report.
 418      */
 419     private JComponent createHtmlBlock() {
 420         JPanel p = uif.createPanel("nrd.htmlops", new GridBagLayout(),
 421                                     false);
 422         p.setName("htmlops");
 423         GridBagConstraints gbc = new GridBagConstraints();
 424         gbc.fill = GridBagConstraints.NONE;
 425         gbc.anchor = GridBagConstraints.WEST;
 426         gbc.gridwidth = 2;
 427         gbc.gridy = 0;
 428 
 429         // info about this block of buttons
 430         JTextArea info = uif.createMessageArea("nrd.htmlops");
 431         //info.setBorder(BorderFactory.createLineBorder(Color.black));
 432         p.add(info, gbc);
 433 
 434         // checkboxes
 435         // these are given settings here, but setting will be reloaded
 436         // elsewhere before the user sees them
 437         JCheckBox cb = uif.createCheckBox("nrd.htmlops.config", true);
 438         cbConfig = cb;
 439         cbConfig.addActionListener(cbHtmlListener);
 440         htmlGroup.add(cb);
 441         gbc.gridy = 1;
 442         p.add(cb, gbc);
 443 
 444         // question log
 445         gbc.gridwidth = 1;
 446         gbc.gridy = 2;
 447         cb = cbQl = uif.createCheckBox("nrd.htmlops.ql", true);
 448         configGroup.add(cb);
 449         htmlGroup.add(cb);
 450         p.add(uif.createHorizontalStrut(25), gbc);
 451         p.add(cb, gbc);
 452 
 453         // environment
 454         gbc.gridy = 3;
 455         cb = cbEnv = uif.createCheckBox("nrd.htmlops.env", false);
 456         configGroup.add(cb);
 457         htmlGroup.add(cb);
 458         p.add(uif.createHorizontalStrut(25), gbc);
 459         p.add(cb, gbc);
 460 
 461         // standard values
 462         gbc.gridy = 4;
 463         cb = cbStd = uif.createCheckBox("nrd.htmlops.std", false);
 464         configGroup.add(cb);
 465         htmlGroup.add(cb);
 466         p.add(uif.createHorizontalStrut(25), gbc);
 467         p.add(cb, gbc);
 468 
 469         // result summary
 470         gbc.gridy = 5;
 471         gbc.gridwidth = 2;
 472         cb = cbResults = uif.createCheckBox("nrd.htmlops.res", true);
 473         htmlGroup.add(cb);
 474         p.add(cb, gbc);
 475 
 476         // KFL summary
 477         gbc.gridy = 6;
 478         gbc.gridwidth = 2;
 479         cb = cbKfl = uif.createCheckBox("nrd.htmlops.kfl", true);
 480         htmlGroup.add(cb);
 481         p.add(cb, gbc);
 482 
 483         // keyword summary
 484         cb = cbKws = uif.createCheckBox("nrd.htmlops.kw", true);
 485         htmlGroup.add(cb);
 486         gbc.gridy = 7;
 487         p.add(cb, gbc);
 488 
 489         JScrollPane sp = uif.createScrollPane(p,
 490                 ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
 491                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 492 
 493         sp.setViewportBorder(BorderFactory.createCompoundBorder(
 494             BorderFactory.createEmptyBorder(10, 5, 5, 5),
 495             uif.createTitledBorder("nrd.htmlops")
 496         ));
 497         sp.addPropertyChangeListener("enabled", new PanelEnableListener(sp));
 498         return sp;
 499     }
 500 
 501     /**
 502      * Options for the HTML report files.
 503      */
 504     private JComponent createFilesBlock() {
 505         JPanel p = uif.createPanel("nrd.htmlf", new GridBagLayout(),
 506                                     false);
 507         p.setName("htmlf");
 508         GridBagConstraints gbc = new GridBagConstraints();
 509         gbc.fill = GridBagConstraints.BOTH;
 510         gbc.anchor = GridBagConstraints.WEST;
 511         //gbc.gridx = 0;
 512         gbc.gridwidth = 3;
 513         gbc.gridy = 0;
 514 
 515         // info about this block of buttons
 516         JTextArea info = uif.createMessageArea("nrd.htmlf");
 517         //info.setBorder(BorderFactory.createLineBorder(Color.black));
 518         p.add(info, gbc);
 519 
 520         // settings are given here, but settings will be reloaded
 521         // elsewhere before the user sees them
 522 
 523         // index.html and/or report.html
 524         JLabel lab = uif.createLabel("nrd.htmlf.main", false);
 525         gbc.gridy = 1;
 526         p.add(lab, gbc);
 527 
 528         gbc.gridwidth = 1;
 529         gbc.gridy = 2;
 530         JCheckBox cb = cbHtmlRpt = uif.createCheckBox("nrd.htmlf.rpt", true);
 531         cbHtmlRpt.addActionListener(cbHtmlListener);
 532         htmlGroup.add(cb);
 533         p.add(uif.createHorizontalStrut(25), gbc);
 534         gbc.gridwidth = 2;
 535         p.add(cb, gbc);
 536 
 537         gbc.gridy = 3;
 538         gbc.gridwidth = 1;
 539         cb = cbHtmlInd = uif.createCheckBox("nrd.htmlf.idx", false);
 540         cbHtmlInd.addActionListener(cbHtmlListener);
 541         htmlGroup.add(cb);
 542         p.add(uif.createHorizontalStrut(25), gbc);
 543         gbc.gridwidth = 2;
 544         p.add(cb, gbc);
 545 
 546         // pass, fail, error, notrun files
 547         gbc.gridy = 4;
 548         gbc.gridwidth = 3;
 549         lab = uif.createLabel("nrd.htmlf.xtra", false);
 550         p.add(lab, gbc);
 551 
 552         gbc.gridy = 5;
 553         cb = uif.createCheckBox("nrd.htmlf.pass", true);
 554         htmlGroup.add(cb);
 555         cbPass = cb;
 556         gbc.gridwidth = 1;
 557         p.add(uif.createHorizontalStrut(25), gbc);
 558         p.add(cb, gbc);
 559 
 560         cb = cbErr = uif.createCheckBox("nrd.htmlf.err", true);
 561         htmlGroup.add(cb);
 562         p.add(cb, gbc);
 563 
 564         gbc.gridy = 6;
 565         cb = cbFail = uif.createCheckBox("nrd.htmlf.fail", true);
 566         htmlGroup.add(cb);
 567         p.add(uif.createHorizontalStrut(25), gbc);
 568         p.add(cb, gbc);
 569 
 570         cb = cbNr = uif.createCheckBox("nrd.htmlf.nr", true);
 571         htmlGroup.add(cb);
 572         p.add(cb, gbc);
 573 
 574         JScrollPane sp = uif.createScrollPane(p,
 575                 ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
 576                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 577 
 578         sp.setViewportBorder(BorderFactory.createCompoundBorder(
 579             BorderFactory.createEmptyBorder(10, 5, 5, 5),
 580             uif.createTitledBorder("nrd.htmlf")
 581         ));
 582 
 583         sp.addPropertyChangeListener("enabled", new PanelEnableListener(sp));
 584         return sp;
 585     }
 586 
 587     /**
 588      * Options for COF report
 589      */
 590     private JPanel createCofPane() {
 591         JPanel cofpanel = uif.createPanel("nrd.cofops");
 592         cofpanel.setLayout(new GridBagLayout());
 593         cbCofTestCases = uif.createCheckBox("nrd.cofops.allowtestcases", true);
 594         GridBagConstraints cb = new GridBagConstraints();
 595         cb.anchor = GridBagConstraints.NORTHWEST;
 596         cb.weightx = 1.;
 597         cb.weighty = 1.;
 598         cb.ipadx = 8;
 599         cb.ipady = 8;
 600         cofpanel.add(cbCofTestCases, cb);
 601         cofpanel.addPropertyChangeListener("enabled", new PanelEnableListener(cofpanel));
 602         return cofpanel;
 603     }
 604 
 605     /**
 606      * Options for the KFL report files.
 607      */
 608     private JComponent createKflBlock() {
 609         JPanel p = uif.createPanel("nrd.htmlkfl", new GridBagLayout(),
 610                                     false);
 611         p.setName("htmlkfl");
 612         GridBagConstraints gbc = new GridBagConstraints();
 613         gbc.fill = GridBagConstraints.BOTH;
 614         gbc.anchor = GridBagConstraints.NORTHWEST;
 615         //gbc.gridx = 0;
 616         gbc.gridwidth = 3;
 617         gbc.gridy = 0;
 618 
 619         // info about this block of buttons
 620         JTextArea info = uif.createMessageArea("nrd.htmlkfl");
 621         //info.setBorder(BorderFactory.createLineBorder(Color.black));
 622         p.add(info, gbc);
 623 
 624         // settings are given here, but settings will be reloaded
 625         // elsewhere before the user sees them
 626 
 627         // general KFL options
 628         JLabel lab = uif.createLabel("nrd.htmlkfl.ops", false);
 629         gbc.gridy = 1;
 630         p.add(lab, gbc);
 631 
 632         gbc.gridwidth = 1;
 633         gbc.gridy = 2;
 634 
 635         JCheckBox cb = cbKflTc = uif.createCheckBox("nrd.htmlkfl.checktc", true);
 636         cbKflTc.addActionListener(cbHtmlListener);
 637         cbKflTc.setSelected(true);
 638         kflGroup.add(cb);
 639         p.add(uif.createHorizontalStrut(25), gbc);
 640         gbc.gridwidth = 2;
 641         p.add(cb, gbc);
 642 
 643         // kfl file options
 644         gbc.gridy = 3;
 645         gbc.gridwidth = 3;
 646         lab = uif.createLabel("nrd.htmlkfl.xtra", false);
 647         p.add(lab, gbc);
 648 
 649         gbc.gridy = 4;
 650         gbc.gridwidth = 1;
 651 
 652         cb = cbKflF2f = uif.createCheckBox("nrd.htmlkfl.f2f", true);
 653         cbKflF2f.addActionListener(cbHtmlListener);
 654         cbKflF2f.setSelected(true);
 655         kflGroup.add(cb);
 656         p.add(uif.createHorizontalStrut(25), gbc);
 657         gbc.gridwidth = 2;
 658         p.add(cb, gbc);
 659 
 660         gbc.gridy = 5;
 661         gbc.gridwidth = 1;
 662         cb = cbKflF2e = uif.createCheckBox("nrd.htmlkfl.f2e", false);
 663         cbKflF2e.addActionListener(cbHtmlListener);
 664         cbKflF2e.setSelected(true);
 665         htmlGroup.add(cb);
 666         p.add(uif.createHorizontalStrut(25), gbc);
 667         gbc.gridwidth = 2;
 668         p.add(cb, gbc);
 669 
 670         gbc.gridy = 6;
 671         gbc.gridwidth = 1;
 672         cb = cbKflMissing = uif.createCheckBox("nrd.htmlkfl.missing", false);
 673         cbKflMissing.addActionListener(cbHtmlListener);
 674         cbKflMissing.setSelected(true);
 675         htmlGroup.add(cb);
 676         p.add(uif.createHorizontalStrut(25), gbc);
 677         gbc.gridwidth = 2;
 678         p.add(cb, gbc);
 679 
 680         JScrollPane sp = uif.createScrollPane(p,
 681                 ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
 682                 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 683 
 684         sp.setViewportBorder(BorderFactory.createCompoundBorder(
 685             BorderFactory.createEmptyBorder(10, 5, 5, 5),
 686             uif.createTitledBorder("nrd.htmlkfl")
 687         ));
 688 
 689         sp.addPropertyChangeListener("enabled", new PanelEnableListener(sp));
 690         return sp;
 691     }
 692 
 693     private JPanel createBackupOpsPane() {
 694         JPanel p = uif.createPanel("nrd.backup", new GridBagLayout(),
 695                                     false);
 696         p.setName("backup");
 697         GridBagConstraints gbc = new GridBagConstraints();
 698         gbc.fill = GridBagConstraints.BOTH;
 699         gbc.anchor = GridBagConstraints.WEST;
 700         gbc.gridx = 0;
 701         gbc.gridwidth = 3;
 702 
 703         // info about this block of buttons
 704         JTextArea info = uif.createMessageArea("nrd.backup");
 705         //info.setBorder(BorderFactory.createLineBorder(Color.black));
 706         p.add(info, gbc);
 707 
 708         // checkboxes
 709         // these are given settings here, but setting will be reloaded
 710         // elsewhere before the user sees them
 711         JCheckBox cb = cbBak = uif.createCheckBox("nrd.backup.bak", true);
 712         cbBak.addChangeListener(new ChangeListener() {
 713                 public void stateChanged(ChangeEvent e) {
 714                     if (numBak != null) {
 715                         numBak.setEnabled(cbBak.isSelected());
 716                     }
 717                 }
 718             });
 719         p.add(cb, gbc);
 720 
 721         gbc.gridwidth = 1;
 722         gbc.gridy = 2;
 723         gbc.gridx = GridBagConstraints.RELATIVE;
 724         p.add(uif.createHorizontalStrut(25), gbc);
 725 
 726         JLabel lab = uif.createLabel("nrd.backup.bak.level", true);
 727         p.add(lab, gbc);
 728 
 729         // backup levels
 730         numBak = uif.createInputField("nrd.backup.bak.level", 2, lab);
 731         gbc.insets.left = 12;
 732         numBak.setText("1");
 733 
 734         Action action = new AbstractAction() {
 735             public void actionPerformed(ActionEvent e) {
 736                     JTextField src = (JTextField) (e.getSource());
 737                     String cmd = e.getActionCommand();
 738                     char ch = cmd.charAt(0);
 739                     int savedDot = src.getCaret().getDot();
 740                     int savedMark = src.getCaret().getMark();
 741                     String savedText = src.getText();
 742 
 743                     // reject all non-numeric chars
 744                     if (Character.isDigit(ch) && ch != '0') {
 745                         Action delegate = new javax.swing.text.DefaultEditorKit.InsertContentAction();
 746                         delegate.actionPerformed(e);
 747 
 748                         String newText = src.getText();
 749                         // reject new input if it leaves an invalid value,
 750                         // i.e. more than one digit (>9)
 751                         if (newText.length() > 1) {
 752                             src.setText(savedText);
 753                         }
 754                     }
 755                 }
 756             };
 757         Keymap keymap = numBak.addKeymap("intField", numBak.getKeymap());
 758         keymap.setDefaultAction(action);
 759         numBak.setKeymap(keymap);
 760 
 761         p.add(numBak, gbc);
 762 
 763         p.setMaximumSize(p.getPreferredSize());
 764         return p;
 765     }
 766 
 767 
 768     /**
 769      * @return number of custom reports added
 770      */
 771     private int getCustomReports(JPanel p) {
 772         int result = 0;
 773 
 774         ContextManager cm = model.getContextManager();
 775         if (cm == null)
 776             return 0;
 777 
 778         customReports = cm.getCustomReports();
 779         if (customReports == null || customReports.length == 0) {
 780             customReports = null;
 781             return 0;
 782         }
 783 
 784         customBoxes = new HashMap<>();
 785 
 786         for (int i = 0; i < customReports.length; i++) {
 787             JCheckBox cb = new JCheckBox(customReports[i].getName());
 788             cb.setName(customReports[i].getReportId());
 789             listModel.addElement(cb);
 790             customBoxes.put(cb, customReports[i]);
 791 
 792             ReportConfigPanel[] ops = customReports[i].getOptionPanes();
 793             if (ops == null || ops.length == 0) {
 794                 // no config panels, use blank
 795                 p.add(customReports[i].getReportId(),
 796                         uif.createPanel("nrd.blank", false));
 797             }
 798             else {
 799                 // tabbed pane for all supplied panels
 800                 JTabbedPane tp = uif.createTabbedPane("nrd.custom.tabs");
 801                 for (int j = 0; j < ops.length; j++)
 802                     tp.addTab(ops[j].getPanelName(), ops[j]);
 803 
 804                 p.add(customReports[i].getReportId(), tp);
 805             }
 806             result++;
 807         }   // for
 808 
 809         return result;
 810     }
 811 
 812 
 813     /**
 814      * Walks through the list of check boxes.
 815      * @return false iff none of check boxes is selected, true otherwise
 816      */
 817     private boolean hasSelectedCheckBox() {
 818         for (int i = 0; i < listModel.getSize(); i++) {
 819             if (((JCheckBox)listModel.getElementAt(i)).isSelected()) {
 820                 return true;
 821             }
 822         }
 823         return false;
 824     }
 825 
 826 
 827     private void showReportChooserDialog() {
 828         ReportDirChooser rdc = getReportDirChooser();
 829         rdc.setMode(ReportDirChooser.NEW);
 830         int option = rdc.showDialog(dirField);
 831         if (option != JFileChooser.APPROVE_OPTION)
 832             return;
 833 
 834         dirField.setText(rdc.getSelectedFile().getAbsolutePath());
 835     }
 836 
 837     private ReportDirChooser getReportDirChooser() {
 838         if (reportDirChooser == null)
 839             reportDirChooser = new ReportDirChooser();
 840         return reportDirChooser;
 841     }
 842 
 843     void showReportBrowser(File f) {
 844         if(f.equals(new File(reportDir, Report.INDEX_FILE_NAME))) {
 845             reportBrowser.show(f);
 846         }
 847         else {
 848             String[] names = Report.getHtmlReportFilenames();
 849             ArrayList<File> possible = new ArrayList<>();
 850 
 851             for (int i = 0; i < names.length; i++) {
 852                 File rpt = new File(reportDir, names[i]);
 853                 if (rpt.exists() && rpt.canRead())
 854                     possible.add(rpt);
 855                 else
 856                     rpt = null;
 857             }
 858 
 859             if (possible.size() > 0) {
 860                 // show the most recently dated file
 861                 File rpt = null;
 862                 File newestF = null;
 863                 long newestTime = 0;
 864 
 865                 for (int i = 0; i < possible.size(); i++) {
 866                     File file = (File)(possible.get(i));
 867                     if (file.lastModified() > newestTime) {
 868                         newestF = file;
 869                         newestTime = file.lastModified();
 870                     }
 871                 }       // for
 872                 reportBrowser.show(newestF);
 873             }
 874             else
 875                 reportBrowser.show(reportDir);
 876         }
 877     }
 878 
 879     /**
 880      * Normally called by the background thread.
 881      */
 882     private void runReport(ReportSettings settings) throws IOException {
 883         if (interviewParams != null) {
 884             Report r = new Report();
 885 
 886             // if no work directory selected, throw exception.
 887             if (interviewParams.getWorkDirectory() == null) {
 888                 throw new IOException(uif.getI18NString("nrd.noWorkDir.err"));
 889             }
 890             r.addStartGenListener(new ReportGenListener());
 891             settings.setCustomReports(getActiveCustomReports());
 892             r.writeReports(settings, reportDir );
 893 
 894         }
 895         else {
 896             throw new IOException(uif.getI18NString("nrd.nullParams.err"));
 897         }
 898     }
 899 
 900     private ArrayList<CustomReport> getActiveCustomReports() {
 901 
 902         ArrayList<CustomReport> customReps = new ArrayList<>();
 903         if (customBoxes != null && customBoxes.size() > 0) {
 904             Iterator it = customBoxes.keySet().iterator();
 905             while (it.hasNext()) {
 906                 JCheckBox box = (JCheckBox)(it.next());
 907                 if (box.isSelected()) {
 908                     customReps.add(customBoxes.get(box));
 909                 }
 910             }
 911         }
 912         return customReps;
 913     }
 914 
 915     private boolean isEmptyDirectory(File f) {
 916         return (f.isDirectory() && f.list().length == 0);
 917     }
 918 
 919     private void updateHtmlCheckboxStates() {
 920         // config buttons done before html buttons to ensure that
 921         // config gets disabled if HTML reporting is off
 922 
 923         if (!cbHtml.isSelected()) {
 924             for (int i = 0; i < htmlGroup.size(); i++)
 925                 ((JCheckBox)(htmlGroup.get(i))).setEnabled(false);
 926         }
 927         else {
 928             for (int i = 0; i < htmlGroup.size(); i++)
 929                 ((JCheckBox)(htmlGroup.get(i))).setEnabled(true);
 930             for (int i = 0; i < configGroup.size(); i++)
 931                 ((JCheckBox)(configGroup.get(i))).setEnabled(cbConfig.isSelected());
 932         }
 933     }
 934 
 935 
 936     private void updateCofCheckboxStates() {
 937         if (!cbCof.isSelected()) {
 938             cbCofTestCases.setEnabled(false);
 939         } else {
 940             cbCofTestCases.setEnabled(true);
 941         }
 942     }
 943 
 944     private ReportSettings captureState() {
 945         ReportSettings snap = new ReportSettings(interviewParams);
 946 
 947         snap.setEnableHtmlReport(cbHtml.isSelected());
 948         snap.setEnableXmlReport(cbXml.isSelected());
 949         snap.setEnablePlainReport(cbPlain.isSelected());
 950         snap.setEnableCOFReport(cbCof.isSelected());
 951 
 952         snap.setUseTestCases(cbCofTestCases.isSelected());
 953 
 954         snap.setShowConfigSection(cbConfig.isSelected());
 955         snap.setShowEnvLog(cbEnv.isSelected());
 956         snap.setShowQuestionLog(cbQl.isSelected());
 957         snap.setShowStdValues(cbStd.isSelected());
 958         snap.setShowResults(cbResults.isSelected());
 959         snap.setShowKflReport(cbKfl.isSelected());
 960         snap.setShowKeywordSummary(cbKws.isSelected());
 961 
 962         snap.setHtmlMainReport(cbHtmlRpt.isSelected(), cbHtmlInd.isSelected());
 963         snap.setEnableHtmlStateFile(Status.PASSED, cbPass.isSelected());
 964         snap.setEnableHtmlStateFile(Status.FAILED, cbFail.isSelected());
 965         snap.setEnableHtmlStateFile(Status.ERROR, cbErr.isSelected());
 966         snap.setEnableHtmlStateFile(Status.NOT_RUN, cbNr.isSelected());
 967 
 968         snap.setEnableKflF2e(cbKflF2e.isSelected());
 969         snap.setEnableKflF2f(cbKflF2f.isSelected());
 970         snap.setEnableKflMissing(cbKflMissing.isSelected());
 971         snap.setEnableKflTestCases(cbKflTc.isSelected());
 972 
 973         snap.setEnableBackups(cbBak.isSelected());
 974 
 975         try {
 976             int i = Integer.parseInt(numBak.getText());
 977             snap.setBackupLevels(i);
 978         }
 979         catch (NumberFormatException e) {
 980         }
 981 
 982         TestFilter filter = filterHandler.getActiveFilter();
 983 
 984         // see Report.Settings for info on what this is:
 985         /*
 986           // commented out because Optimization will be enforced
 987           // when filter is ParameterFilter
 988         if (filter instanceof ParameterFilter)
 989             snap.setAllowInitFilesOptimize(true);
 990         else
 991             snap.setAllowInitFilesOptimize(false);
 992          */
 993 
 994         snap.setFilter(filter);
 995 
 996         return snap;
 997     }
 998 
 999     private void setState(ReportSettings snap) {
1000         if (snap == null)
1001             return;
1002 
1003         lastSettings = snap;
1004 
1005 
1006         cbHtml.setSelected(snap.isHtmlEnabled());
1007         cbXml.setSelected(snap.isXmlEnabled());
1008         cbPlain.setSelected(snap.isPlainEnabled());
1009         boolean isCofEnabled = snap.isCOFEnabled();
1010         cbCof.setSelected(isCofEnabled);
1011 
1012         cbCofTestCases.setSelected(snap.isCOFTestCasesEnabled());
1013         cbCofTestCases.setEnabled(isCofEnabled);
1014 
1015         // ---
1016         boolean section = snap.isConfigSectionEnabled();
1017         cbConfig.setSelected(section);
1018 
1019         cbEnv.setSelected(snap.isEnvEnabled());
1020         cbQl.setSelected(snap.isQuestionLogEnabled());
1021         cbStd.setSelected(snap.isStdEnabled());
1022 
1023         cbEnv.setEnabled(section);
1024         cbQl.setEnabled(section);
1025         cbStd.setEnabled(section);
1026 
1027         // ---
1028         cbResults.setSelected(snap.isResultsEnabled());
1029         cbKfl.setSelected(snap.isKflEnabled());
1030         cbKws.setSelected(snap.isKeywordSummaryEnabled());
1031 
1032         cbHtmlRpt.setSelected(snap.isReportHtmlEnabled());
1033         cbHtmlInd.setSelected(snap.isIndexHtmlEnabled());
1034         cbPass.setSelected(snap.isStateFileEnabled(Status.PASSED));
1035         cbFail.setSelected(snap.isStateFileEnabled(Status.FAILED));
1036         cbErr.setSelected(snap.isStateFileEnabled(Status.ERROR));
1037         cbNr.setSelected(snap.isStateFileEnabled(Status.NOT_RUN));
1038 
1039         cbKflF2e.setSelected(snap.isKflF2eEnabled());
1040         cbKflF2f.setSelected(snap.isKflF2fEnabled());
1041         cbKflMissing.setSelected(snap.isKflMissingEnabled());
1042         cbKflTc.setSelected(snap.isKflTestCasesEnabled());
1043 
1044         cbBak.setSelected(snap.isBackupsEnabled());
1045         numBak.setText(Integer.toString(snap.getBackupLevel()));
1046 
1047         // a null value will be ignored
1048         filterHandler.setFilter(snap.getTestFilter());
1049     }
1050 
1051     private ActionListener cbHtmlListener = new ActionListener() {
1052         public void actionPerformed(ActionEvent e) {
1053             Object src = e.getSource();
1054 
1055             if (src == cbHtml || src == cbConfig)
1056                 updateHtmlCheckboxStates();
1057             else if (src == cbBak) {
1058                 numBak.setEnabled(cbBak.isSelected());
1059                 } else if (src == cbCof)
1060                         updateCofCheckboxStates();
1061         }
1062     };
1063 
1064     private ActionListener listener = new ActionListener() {
1065         public void actionPerformed(ActionEvent e) {
1066             Object src = e.getSource();
1067 
1068             if (src == browseBtn) {
1069                 showReportChooserDialog();
1070             }
1071             else if (src == cancelBtn) {
1072                 // revert settings
1073                 // hide dialog
1074                 setVisible(false);
1075 
1076                 setState(lastSettings);
1077                 if (okBtn != null) {
1078                     okBtn.setEnabled(hasSelectedCheckBox());
1079                 }
1080             }
1081             else if (src == okBtn) {
1082                 String s = (String) dirField.getText();
1083 
1084                 // check null input
1085                 if (s == null || s.length() == 0) {
1086                     uif.showError("nrd.emptyInput");
1087                     return;
1088                 }
1089 
1090                 // check for empty input
1091                 if (cbBak.isSelected() && numBak.getText().equals("")) {
1092                     uif.showError("nrd.emptyBak");
1093                     // select the last tab (assumes this has the backup settings)
1094                     tabs.setSelectedIndex(tabs.getTabCount()-1);
1095                     return;
1096                 }
1097 
1098                 // validate custom reports
1099                 Iterator it = getActiveCustomReports().iterator();
1100                 while (it.hasNext()) {
1101                     CustomReport cr = (CustomReport) it.next();
1102                     String error = cr.validateOptions();
1103                     if (error != null) {
1104                         for (int i = 0; i < listModel.getSize(); i++ ) {
1105                             JCheckBox cb = listModel.elementAt(i);
1106                             if (cb.getName().equals(cr.getReportId())) {
1107                                 list.setSelectedIndex(i);
1108                             }
1109                         }
1110                         uif.showError("nrd.optionsErr", new Object[] {error} );
1111                         return;
1112                     }
1113                 }
1114 
1115                 reportDir = new File(s);
1116 
1117                 try {
1118                     // check if dir needs to be created.
1119                     if (!reportDir.isDirectory()) {
1120                         reportDir.mkdirs();
1121                     }
1122                     else {
1123                             // check if dir is a report dir
1124                         if (!Report.isReportDirectory(reportDir) &&
1125                             !isEmptyDirectory(reportDir)) {
1126                             uif.showError("nrd.cantUse", reportDir);
1127                             return;
1128                         }
1129                     }
1130 
1131                     // XXX save settings in prefs
1132 
1133                     setVisible(false);
1134                     lastSettings = captureState();
1135                     notifyStarting();
1136                     doBgReport(lastSettings);
1137                 }
1138                 catch (SecurityException se) {
1139                     uif.showError("nrd.cantCreate", se.getMessage());
1140                 }
1141             }
1142         }
1143 
1144         // does work on background thread
1145         private void doBgReport(final ReportSettings snap) {
1146             /*final JDialog*/ waitDialog = uif.createWaitDialog("nrd.wait", parent);
1147 
1148             final Stopper stopper = new Stopper();
1149             final Thread worker = new Thread() {
1150                 public void run() {
1151                     try {
1152                         runReport(snap);
1153                         // go away to switch back to GUI thread
1154                         finishReport(waitDialog, snap, stopper);
1155                     }
1156                     catch (CustomReport.ReportException ie) {
1157                         showError("nrd.custom", ie.getMessage(), waitDialog);
1158                         notifyError(ie.getMessage());
1159                     }
1160                     catch (IOException ie) {
1161                         showError("nrd.cantWrite", ie.getMessage(), waitDialog);
1162                         notifyError(ie.getMessage());
1163                         // should reshow dialog
1164                     }
1165                     catch (SecurityException se) {
1166                         showError("nrd.cantCreate", se.getMessage(), waitDialog);
1167                         notifyError(se.getMessage());
1168                         // should reshow dialog
1169                     }
1170                     catch (RuntimeException re) {
1171                         showError("nrd.errorWriting", re.getMessage(), waitDialog);
1172                         notifyErrorWriting(re.getMessage());
1173                     }
1174                 }   // run()
1175             };  // thread
1176 
1177             // this code is copied from ReportTool....
1178             GridBagConstraints gbc = new GridBagConstraints();
1179             gbc.fill = GridBagConstraints.NONE;
1180             gbc.anchor = GridBagConstraints.CENTER;
1181             gbc.insets.bottom = 10;
1182             gbc.insets.top = 10;
1183             gbc.gridy = 2;
1184             gbc.gridx = 0;
1185             JButton cancelBtn = uif.createButton("nrd.cancel");
1186             waitDialog.getContentPane().add(cancelBtn, gbc);
1187             waitDialog.pack();
1188             final String cancelling = uif.getI18NString("nrd.cancelling");
1189             cancelBtn.addActionListener( new ActionListener() {
1190                 public void actionPerformed(ActionEvent e) {
1191                     JButton butt = (JButton) e.getSource();
1192                     butt.setEnabled(false);
1193                     Component[] cmp = waitDialog.getContentPane().getComponents();
1194                     if (worker != null && worker.isAlive()) {
1195                         stopper.waitWasHidden = true;
1196                         worker.interrupt();
1197                     }
1198                     for ( int i = 0; i < cmp.length; i++) {
1199                         if("nrd.wait".equals(cmp[i].getName())) {
1200                             if (cmp[i] instanceof JTextComponent) {
1201                                 ((JTextComponent)cmp[i]).setText(cancelling);
1202                             }
1203                             break;
1204                         }
1205                     }
1206 
1207                 }
1208             });
1209 
1210 
1211 
1212 
1213             ActionListener al = new ActionListener() {
1214                 public void actionPerformed(ActionEvent evt) {
1215                     // show dialog if still processing
1216                     if (worker.isAlive()) {
1217                         waitDialog.show();
1218                     }
1219                 }
1220             };
1221 
1222             // show wait dialog if operation is still running after
1223             // WAIT_DIALOG_DELAY
1224             Timer timer = new Timer(WAIT_DIALOG_DELAY, al);
1225             timer.setRepeats(false);
1226             timer.start();
1227 
1228             // do it!
1229             worker.start();
1230         }
1231 
1232         private void finishReport(final JDialog waitDialog,
1233                                   final ReportSettings snap, final Stopper stopper) {
1234             // done generating report, switch back to GUI thread
1235             EventQueue.invokeLater(new Runnable() {
1236                     public void run() {
1237                         waitDialog.hide();
1238                         if(!stopper.waitWasHidden) {
1239                             int r = uif.showYesNoDialog("nrd.showReport");
1240 
1241                             switch (r) {
1242                             case JOptionPane.YES_OPTION:
1243                                 File index = new File(reportDir, Report.INDEX_FILE_NAME);
1244                                 if(index.exists() && index.canRead()) {
1245                                     showReportBrowser(index);
1246                                 }
1247                                 else {
1248                                     showReportBrowser(selectFileToShow(snap));
1249                                 }
1250                                 break;
1251                             case JOptionPane.NO_OPTION:
1252                                 break;
1253                             default:
1254                                 break;
1255                             }
1256                         }
1257 
1258                         notifyDone();
1259                         notifyUpdate(getLastState());
1260                     }
1261                 }
1262             );
1263         }
1264 
1265 
1266         /**
1267          * @param uiKey Key to use to call <tt>UIFactory.showError()</tt>
1268          * @param msg The localized error message to show.
1269          */
1270         private void showError(final String uiKey, final String msg,
1271                                 final JDialog waitDialog) {
1272             // switch back to GUI thread
1273             EventQueue.invokeLater(new Runnable() {
1274                     public void run() {
1275                         waitDialog.hide();
1276                         uif.showError(uiKey, msg);
1277                     }
1278                 }
1279             );
1280         }
1281 
1282         /**
1283          * Decide which file in the report dir to show to the user when
1284          * they ask to view a generated 'report'.
1285          */
1286         private File selectFileToShow(ReportSettings snap) {
1287             File result = new File(reportDir, Report.INDEX_FILE_NAME);
1288 
1289             if(!result.exists()) {
1290                 if (snap.isHtmlEnabled()) {
1291                     // show main report file or file listing
1292                     result = searchForFile(reportDir, Report.getHtmlReportFilenames());
1293                     if (result == null)
1294                         result = reportDir;
1295                 }
1296                 /* not useful right now to see raw xml, could do XSLT later
1297                 else if (snap.isPlainEnabled()) {
1298                     // show that file
1299                     result = searchForFile(reportDir, Report.getPlainReportFilenames());
1300                     if (result == null)
1301                         result = reportDir;
1302                 }
1303                 else if (snap.isXmlEnabled()) {
1304                     result = new File(reportDir, "xml");
1305                 }
1306                 */
1307                 else {
1308                     result = reportDir;
1309                 }
1310             }
1311 
1312             return result;
1313         }
1314 
1315         private File searchForFile(File dir, String[] names) {
1316             for (int i = 0; i < names.length; i++) {
1317                 File f = new File(reportDir, names[i]);
1318                 if (f.exists()) {
1319                     return f;
1320                 }
1321             }   // for
1322 
1323             return null;
1324         }
1325     };
1326 
1327 
1328     //------------------------------end of listener--------------------------
1329 
1330     private static class Stopper {
1331         boolean waitWasHidden = false;
1332     }
1333 
1334     private class ReportGenListener implements StartGenListener {
1335         public void startReportGeneration(ReportSettings s, String reportID) {
1336             String reportName;
1337             if(reportID.equals("xml") || reportID.equals("pt") ||
1338                                                 reportID.equals("html")) {
1339                 reportName = uif.getI18NString("nrd.type." + reportID + ".ckb");
1340             }
1341             else {
1342                 reportName = reportID;
1343             }
1344 
1345             String status = uif.getI18NString("nrd.wait.report_gen",
1346                                                 new String[] {reportName});
1347 
1348             Component[] cmp = waitDialog.getContentPane().getComponents();
1349             for ( int i = 0; i < cmp.length; i++) {
1350                 if("nrd.wait".equals(cmp[i].getName())) {
1351                     if (cmp[i] instanceof JTextComponent) {
1352                         ((JTextComponent)cmp[i]).setText(status);
1353                     }
1354                     break;
1355                 }
1356             }
1357         }
1358     }
1359 
1360     /**
1361      * This listener changes options state against checkboxes
1362      */
1363     private class SelectListener extends MouseInputAdapter implements KeyListener, ListSelectionListener {
1364 
1365         /**
1366          * @param lst JList of checkboxes
1367          * @param p parent Panel
1368          * @param cardLayout The CardLayout for options
1369          */
1370         SelectListener(JList lst, JPanel p, CardLayout cardLayout ) {
1371             list = lst;
1372             listModel = list.getModel();
1373             lastSelected = listModel.getElementAt(0);
1374             panel = p;
1375             cards = cardLayout;
1376         }
1377 
1378         public void keyTyped(KeyEvent e) {
1379             if (e.getKeyChar() == ' ') {
1380                 process(list.getSelectedIndex());
1381             }
1382         }
1383 
1384         @Override
1385         public void mouseClicked(MouseEvent e) {
1386             if (e.getPoint().getX() <= emptyCBW) {
1387                 process(list.locationToIndex(e.getPoint()));
1388             }
1389         }
1390 
1391         public void valueChanged(ListSelectionEvent e) {
1392             int index = list.getSelectedIndex();
1393             if (index < 0) {
1394                     // no element selected in the list
1395                 return;
1396             }
1397             JCheckBox box = (JCheckBox)(listModel.getElementAt(index));
1398 
1399             if (lastSelected != box) {
1400                 cards.show(panel, box.getName());
1401                 lastSelected = box;
1402             }
1403             enablePanel(box);
1404         }
1405 
1406         private void enablePanel(final JCheckBox box) {
1407             for (int i = 0; i < panel.getComponentCount(); i++) {
1408                 JComponent tab = (JComponent) panel.getComponent(i);
1409                 tab.setEnabled(box.isSelected());
1410             }
1411             if (customBoxes != null) {
1412                 CustomReport rep = (CustomReport) customBoxes.get(box);
1413                 if (rep != null && rep.getOptionPanes() != null) {
1414                     for (int i = 0; i < rep.getOptionPanes().length; i++) {
1415                         rep.getOptionPanes()[i].setEnabled(box.isSelected());
1416                     }
1417                 } else if (box == cbHtml) {
1418                     for (int i = 0; i < panes.length; i++) {
1419                         panes[i].setEnabled(box.isSelected());
1420                     }
1421                 }
1422             }
1423 
1424             if (box == cbHtml)
1425                 infoArea.setText(uif.getI18NString("nrd.info.html.txt"));
1426             else if (box == cbPlain)
1427                 infoArea.setText(uif.getI18NString("nrd.info.plain.txt"));
1428             else if (box == cbXml)
1429                 infoArea.setText(uif.getI18NString("nrd.info.xml.txt"));
1430             else if (box == cbCof)
1431                 infoArea.setText(uif.getI18NString("nrd.info.cof.txt"));
1432             else if (customBoxes != null) {
1433                 CustomReport rep = (CustomReport) customBoxes.get(box);
1434                 if (rep != null)
1435                     infoArea.setText(rep.getDescription());
1436             }
1437 
1438         }
1439 
1440         private void process(final int index) {
1441             if (index < 0) {
1442                 return;
1443             }
1444             JCheckBox box = (JCheckBox)(listModel.getElementAt(index));
1445 
1446             if (lastSelected == box) {
1447                 box.doClick();
1448                 if (okBtn != null) {
1449                     okBtn.setEnabled(hasSelectedCheckBox());
1450                 }
1451                 if (box == cbHtml) {
1452                     updateHtmlCheckboxStates();
1453                 }
1454                 if (box == cbCof) {
1455                     updateCofCheckboxStates();
1456                 }
1457                 list.repaint();     // important!
1458                 enablePanel(box);
1459             }
1460             lastSelected = box;
1461         }
1462 
1463         public void keyReleased(KeyEvent e) {
1464         }
1465 
1466         public void keyPressed(KeyEvent e) {
1467         }
1468 
1469         /**
1470          * Sets the button to change enable/disable state. If none of
1471          * check boxes is selected, the button will be disabled until a check
1472          * box is selected.
1473          * @param okBtn - button
1474          */
1475         private void setOkBtn(JButton okBtn) {
1476             this.okBtn = okBtn;
1477         }
1478 
1479         Object lastSelected;
1480         JList list;
1481         ListModel listModel;
1482         JPanel panel;
1483         CardLayout cards;
1484         JButton okBtn = null; // should be disable iff all check boxes are off
1485         double emptyCBW = new JCheckBox("").getPreferredSize().getWidth() + 2;
1486     }
1487 
1488     /*
1489      * PropertyChangeListener for enabling/disabling container's content
1490      */
1491     private class PanelEnableListener implements PropertyChangeListener {
1492         /**
1493          * @param container Container for controlling
1494          */
1495         PanelEnableListener(Container container) {
1496             theContainer = container;
1497         }
1498         /**
1499          * Catches changes of "enabled" property
1500          * and changes enabled status for all child components
1501          */
1502         public void propertyChange(PropertyChangeEvent evt) {
1503             if ("enabled".equals(evt.getPropertyName())) {
1504                 boolean oldV = ((Boolean) evt.getOldValue()).booleanValue();
1505                 boolean newV = ((Boolean) evt.getNewValue()).booleanValue();
1506                 if (oldV && !newV) {
1507                     // disable
1508                     Iterator chIt = collectChildren(theContainer, new ArrayList<Component>()).iterator();
1509                     enabledComp = new HashSet<>();
1510                     while (chIt.hasNext()) {
1511                         Component c = (Component) chIt.next();
1512                         if (c.isEnabled()) {
1513                             enabledComp.add(c);
1514                             c.setEnabled(false);
1515                         }
1516                     }
1517 
1518                 } else if (!oldV && newV && enabledComp != null) {
1519                     // enable
1520                     Iterator chIt = collectChildren(theContainer, new ArrayList<Component>()).iterator();
1521                     while (chIt.hasNext()) {
1522                         Component c = (Component) chIt.next();
1523                         if (enabledComp.contains(c)) {
1524                             c.setEnabled(true);
1525                         }
1526                     }
1527                 }
1528             }
1529         }
1530 
1531         /**
1532          * Recursively gathers all children components
1533          */
1534         private Collection collectChildren(Container comp, Collection<Component> c) {
1535             Component [] ch = comp.getComponents();
1536             for(int i = 0; i < ch.length; i++) {
1537                 c.add(ch[i]);
1538                 if (ch[i] instanceof Container) {
1539                     collectChildren((Container) ch[i],  c);
1540                 }
1541             }
1542             return c;
1543         }
1544 
1545         private Container theContainer;
1546         private HashSet<Component> enabledComp;
1547     }
1548 
1549     private class CheckBoxListCellRenderer implements ListCellRenderer<JComponent> {
1550         public Component getListCellRendererComponent(
1551                     JList<? extends JComponent> list,
1552                     JComponent value,
1553                     int index,
1554                     boolean isSelected,
1555                     boolean cellHasFocus) {
1556             // assert: value is a JCheckBox
1557             JComponent comp = value;
1558             if (isSelected) {
1559                 comp.setOpaque(true);
1560                 comp.setBackground(list.getSelectionBackground());
1561                 comp.setForeground(list.getSelectionForeground());
1562             }
1563             else {
1564                 comp.setOpaque(false);
1565                 comp.setForeground(list.getForeground());
1566             }
1567 
1568             return comp;
1569          }
1570     }
1571 
1572     private Observer[] obs = new Observer[0];
1573     private ExecModel model;
1574 
1575     private ReportDirChooser reportDirChooser;
1576 
1577     private DefaultListModel<JCheckBox> listModel;
1578     private JList<? extends JComponent> list;
1579     private JTextArea infoArea;
1580     private CustomReport[] customReports;
1581     private HashMap<JCheckBox, CustomReport> customBoxes;
1582 
1583     private JComponent[] panes;
1584     private JCheckBox cbHtml;
1585     private JCheckBox cbPlain;
1586     private JCheckBox cbXml;
1587     private JCheckBox cbCof;
1588 
1589     private JCheckBox cbCofTestCases;
1590 
1591     private JCheckBox cbConfig;
1592     private JCheckBox cbQl;
1593     private JCheckBox cbEnv;
1594     private JCheckBox cbStd;
1595     private JCheckBox cbKfl;
1596     private JCheckBox cbResults;
1597     private JCheckBox cbKws;
1598 
1599     private JCheckBox cbPass;
1600     private JCheckBox cbFail;
1601     private JCheckBox cbErr;
1602     private JCheckBox cbNr;
1603     private JCheckBox cbHtmlRpt;
1604     private JCheckBox cbHtmlInd;
1605 
1606     private JCheckBox cbKflTc;
1607     private JCheckBox cbKflF2f;
1608     private JCheckBox cbKflF2e;
1609     private JCheckBox cbKflMissing;
1610 
1611     private JCheckBox cbBak;
1612     private JTextField numBak;
1613 
1614     private ArrayList<JCheckBox> htmlGroup = new ArrayList<>();
1615     private ArrayList<JCheckBox> configGroup = new ArrayList<>();
1616     private ArrayList<JCheckBox> kflGroup = new ArrayList<>();
1617 
1618     private File reportDir;
1619     private JButton browseBtn;
1620     private JButton okBtn;
1621     private JButton cancelBtn;
1622     private JTextField dirField;
1623 
1624     private JTabbedPane tabs;
1625 
1626     private ReportSettings lastSettings;
1627 
1628     private ReportBrowser reportBrowser;
1629     private InterviewParameters interviewParams;
1630     private FilterSelectionHandler filterHandler;
1631 
1632     // keys for option values used for save & restore
1633     static final String REPORT_DIR = "reportDir";
1634     private static final String FILTER = "filter";
1635     private static final int WAIT_DIALOG_DELAY = 3000;      // 3 second delay
1636 
1637     private JDialog waitDialog;
1638 
1639 }