0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 2004-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.junit.wizards;
0043:
0044: import java.awt.BorderLayout;
0045: import java.awt.Component;
0046: import java.awt.Container;
0047: import java.awt.GridBagConstraints;
0048: import java.awt.GridBagLayout;
0049: import java.awt.GridLayout;
0050: import java.awt.Insets;
0051: import java.awt.Point;
0052: import java.awt.event.ActionEvent;
0053: import java.awt.event.ActionListener;
0054: import java.awt.event.FocusEvent;
0055: import java.awt.event.FocusListener;
0056: import java.awt.event.HierarchyEvent;
0057: import java.awt.event.HierarchyListener;
0058: import java.awt.event.ItemEvent;
0059: import java.awt.event.ItemListener;
0060: import java.awt.event.MouseEvent;
0061: import java.io.File;
0062: import java.util.ArrayList;
0063: import java.util.Arrays;
0064: import java.util.Collection;
0065: import java.util.Iterator;
0066: import java.util.List;
0067: import java.util.Map;
0068: import java.util.ResourceBundle;
0069: import java.util.Set;
0070: import javax.swing.AbstractAction;
0071: import javax.swing.AbstractButton;
0072: import javax.swing.Action;
0073: import javax.swing.ActionMap;
0074: import javax.swing.BorderFactory;
0075: import javax.swing.Box;
0076: import javax.swing.BoxLayout;
0077: import javax.swing.DefaultComboBoxModel;
0078: import javax.swing.JButton;
0079: import javax.swing.JCheckBox;
0080: import javax.swing.JComboBox;
0081: import javax.swing.JComponent;
0082: import javax.swing.JLabel;
0083: import javax.swing.JList;
0084: import javax.swing.JPanel;
0085: import javax.swing.JRootPane;
0086: import javax.swing.JSeparator;
0087: import javax.swing.JTextField;
0088: import javax.swing.ListSelectionModel;
0089: import javax.swing.SwingUtilities;
0090: import javax.swing.border.BevelBorder;
0091: import javax.swing.event.ChangeEvent;
0092: import javax.swing.event.ChangeListener;
0093: import javax.swing.event.DocumentEvent;
0094: import javax.swing.event.DocumentListener;
0095: import javax.swing.event.ListSelectionEvent;
0096: import javax.swing.event.ListSelectionListener;
0097: import javax.swing.event.MouseInputListener;
0098: import javax.swing.text.AbstractDocument;
0099: import javax.swing.text.AttributeSet;
0100: import javax.swing.text.BadLocationException;
0101: import javax.swing.text.DocumentFilter;
0102: import javax.swing.text.NavigationFilter;
0103: import javax.swing.text.Position;
0104: import org.netbeans.api.java.classpath.ClassPath;
0105: import org.netbeans.api.project.Project;
0106: import org.netbeans.api.project.ProjectUtils;
0107: import org.netbeans.api.project.SourceGroup;
0108: import org.netbeans.modules.junit.GuiUtils;
0109: import org.netbeans.modules.junit.JUnitCfgOfCreate;
0110: import org.netbeans.modules.junit.MessageStack;
0111: import org.netbeans.modules.junit.NamedObject;
0112: import org.netbeans.modules.junit.SizeRestrictedPanel;
0113: import org.netbeans.modules.junit.TestCreator;
0114: import org.netbeans.spi.java.project.support.ui.PackageView;
0115: import org.openide.DialogDescriptor;
0116: import org.openide.DialogDisplayer;
0117: import org.openide.ErrorManager;
0118: import org.openide.NotifyDescriptor;
0119: import org.openide.WizardDescriptor;
0120: import org.openide.awt.Mnemonics;
0121: import org.openide.filesystems.FileObject;
0122: import org.openide.filesystems.FileUtil;
0123: import org.openide.loaders.DataObject;
0124: import org.openide.nodes.AbstractNode;
0125: import org.openide.nodes.Children;
0126: import org.openide.nodes.FilterNode;
0127: import org.openide.nodes.Node;
0128: import org.openide.nodes.NodeAcceptor;
0129: import org.openide.nodes.NodeOperation;
0130: import org.openide.util.HelpCtx;
0131: import org.openide.util.NbBundle;
0132: import org.openide.util.UserCancelException;
0133:
0134: /**
0135: *
0136: * @author Marian Petras
0137: */
0138: public final class SimpleTestStepLocation implements
0139: WizardDescriptor.Panel<WizardDescriptor> {
0140:
0141: /**
0142: * message layer for displaying messages about problems with checkbox
0143: * selection
0144: *
0145: * @see MessageStack
0146: */
0147: private static final int MSG_LAYER_CHECKBOXES = 0;
0148: /**
0149: * message layer for displaying messages about problems with classname
0150: *
0151: * @see MessageStack
0152: */
0153: private static final int MSG_LAYER_CLASSNAME = 1;
0154:
0155: private final String testClassNameSuffix = NbBundle.getMessage(
0156: TestCreator.class, "PROP_test_classname_suffix"); //NOI18N
0157:
0158: private Component visualComp;
0159: private List<ChangeListener> changeListeners;
0160: private JTextField tfClassToTest;
0161: private JButton btnBrowse;
0162: private JTextField tfTestClass;
0163: private JTextField tfProjectName;
0164: private JComboBox cboxLocation;
0165: private JTextField tfCreatedFile;
0166:
0167: private JCheckBox chkPublic;
0168: private JCheckBox chkProtected;
0169: private JCheckBox chkPackagePrivate;
0170: private JCheckBox chkSetUp;
0171: private JCheckBox chkTearDown;
0172: private JCheckBox chkMethodBodies;
0173: private JCheckBox chkJavadoc;
0174: private JCheckBox chkHints;
0175:
0176: /** message stack for displaying error messages */
0177: private final MessageStack msgStack = new MessageStack(2);
0178: private String msgClassNameInvalid;
0179: private String msgClassToTestDoesNotExist;
0180: private String msgChkBoxesInvalid;
0181:
0182: /**
0183: * project to create a test class in
0184: */
0185: private Project project;
0186: private WizardDescriptor wizard;
0187:
0188: // focus change detection mechanism
0189:
0190: /**
0191: * true, if the current chosen project have multiple testable SourceGroups.
0192: * If it does, class name entered in the Class to Test textfield must be
0193: * checked agains all of them (to detect ambiguity) and if there are
0194: * multiple classes matching, the user must be forced to choose one
0195: * before leaving the textfield.
0196: * <p>
0197: * The focus change detection mechanism is activated by the
0198: * {@link #hierarchyListener} after this wizard panel is added
0199: * to the wizard dialog. The listener activates the mechanism
0200: * only if the mechanism is
0201: * {@linkplain #focusChangeDetectionEnabled enabled}.
0202: *
0203: * @see #setUp
0204: */
0205: private boolean multipleSourceRoots;
0206: /**
0207: * true if the focus change detection mechanism is enabled.
0208: * Being it enabled does not mean that it is activated
0209: * - it cannot be activated until the visual component is added
0210: * to the wizard dialog
0211: */
0212: private boolean interactionRestrictionsEnabled = false;
0213: /**
0214: * true if the focus change detection mechanism is active
0215: */
0216: private boolean interactionRestrictionsActive = false;
0217: /** <!-- PENDING --> */
0218: private boolean interactionRestrictionsSuspended = false;
0219: /** */
0220: private boolean mouseClicksBlocked = false;
0221: /**
0222: * hierarchy listener that detects when the visual component
0223: * is added to the wizard dialog. Once it is added to the dialog,
0224: * the focus change detection mechanism can be activated.
0225: *
0226: * @see #focusChangeDetectionEnabled
0227: */
0228: private HierarchyListener displayabilityListener;
0229: /** root pane of the wizard dialog */
0230: private JRootPane rootPane;
0231: /**
0232: * default button of the wizard.
0233: * It is actually the default button of the dialog's {@link #rootPane}.
0234: */
0235: private JButton defaultButton;
0236: /**
0237: * action key of the root pane's original default action
0238: * <!-- PENDING -->
0239: */
0240: private String rootPaneDefaultActionKey;
0241: /**
0242: * root pane's original default action
0243: * <!-- PENDING -->
0244: */
0245: private Action rootPaneDefaultAction;
0246: /**
0247: * mouse listener of the wizard dialog's glass pane.
0248: * It is a part of the focus change detection mechanism.
0249: */
0250: private MouseInputListener glassPaneListener;
0251: /**
0252: * UI components on which mouse events are checked and evauluated.
0253: * The mouse events are checked only if there are
0254: * {@link #multipleSourceRoots}.
0255: */
0256: private Component[] mouseBlocked;
0257: /**
0258: * UI components on which mnemonic activation is checked and evaluated.
0259: * Mnemonic activation events are checked only if there are
0260: * {@link #multipleSourceRoots}.
0261: */
0262: private JComponent[] mnemonicBlocked;
0263: /**
0264: * information about actions mapped to action keys of UI components
0265: * accessible using mnemonics.
0266: * This is used for blocking access to those components using
0267: * mnemonics and for restoring the UI components' action maps
0268: * to the original state.
0269: *
0270: * @see #blockMnemonics
0271: * @see #unblockMnemonics
0272: */
0273: private ActionMappingInfo[] actionMappingInfo;
0274: /**
0275: * component that is explicitely allowed to gain focus.
0276: * This is used when a button press event is about to be dispatched
0277: * to the button, so that the focus listener does not interrupt
0278: * focus transfer to the button.
0279: */
0280: private Component focusGainAllowedFor;
0281:
0282: // project structure (static)
0283:
0284: /**
0285: * <code>SourceGroups</code> that have at least one test
0286: * <code>SourceGroup</code> assigned. It is equal to set of keys
0287: * of the {@link #sourcesToTestsMap}.
0288: * It is updated whenever {@link #project} changes.
0289: *
0290: * @see #setUp
0291: */
0292: private SourceGroup[] testableSourceGroups;
0293: /** root folders of {@link #testableSourceGroups} */
0294: private FileObject[] testableSourceGroupsRoots;
0295: /** <!-- PENDING --> */
0296: private SourceGroup[] allTestSourceGroups;
0297: /**
0298: * relation between <code>SourceGroup</code>s
0299: * and their respective test <code>SourceGroup</code>s.
0300: * It is updated whenever {@link #project} changes.
0301: *
0302: * @see #setUp
0303: */
0304: private Map<SourceGroup, Object[]> sourcesToTestsMap;
0305:
0306: // entered and computed data
0307:
0308: /**
0309: * index of the first <code>SourceGroup</code> where a file named
0310: * according to contents of {@link #srcRelFileNameSys} was found.
0311: * The search is performed in {@link #testableSourceGroupsRoots}.
0312: * If such a file is not found in any of the source groups roots,
0313: * this variable is set to <code>-1</code>.
0314: *
0315: * @see #classExists
0316: */
0317: private int sourceGroupParentIndex = -1;
0318: /** */
0319: private FileObject srcFile;
0320: private SourceGroup srcGroup = null;
0321: private String testsRootDirName = ""; //NOI18N
0322: private String srcRelFileNameSys = ""; //NOI18N
0323: private String testRelFileName = ""; //NOI18N
0324: /** */
0325: private FileObject testRootFolder;
0326: /** */
0327: private int classNameLength = 0;
0328: /** length of the string denoting name of the selected SourceGroup */
0329: private boolean srcGroupNameDisplayed = false;
0330: /** <!-- PENDING --> */
0331: private boolean programmaticChange = false;
0332: /** <!-- PENDING --> */
0333: private boolean navigationFilterEnabled = false;
0334: /** <!-- PENDING --> */
0335: private ClsNameNavigationFilter clsNameNavigationFilter;
0336: /** <!-- PENDING --> */
0337: private ClsNameDocumentFilter clsNameDocumentFilter;
0338:
0339: /** */
0340: private boolean ignoreCboxItemChanges = false;
0341: /** */
0342: private boolean ignoreClsNameChanges = false;
0343:
0344: // validation of entered data
0345:
0346: /**
0347: * <code>true</code> if data entered in the form are valid.
0348: * The data are valid if the entered class name denotes an existing
0349: * class and at least one of the <em>Method Access Levels</em>
0350: * checkboxes is selected.
0351: */
0352: private boolean isValid = false;
0353: /** is the class name non-empty and valid? */
0354: private boolean classNameValid = false;
0355: /**
0356: * <code>true</code> if and only if a file named
0357: * according to contents of {@link #srcRelFileNameSys} was found.
0358: * The search is performed in {@link #testableSourceGroupsRoots}.
0359: * If this variable is <code>true</code>, variable
0360: * {@link #sourceGroupParentIndex} is set to a non-negative value.
0361: */
0362: private boolean classExists = false;
0363: /**
0364: * <code>true</code> if and only if at least one of the checkboxes
0365: * in the <em>Method Access Levels</em> group is selected
0366: */
0367: private boolean chkBoxesValid = false;
0368:
0369: //--------------------------------------------------------------------------
0370:
0371: public SimpleTestStepLocation() {
0372: visualComp = createVisualComp();
0373: }
0374:
0375: private Component createVisualComp() {
0376: JLabel lblClassToTest = new JLabel();
0377: JLabel lblCreatedTestClass = new JLabel();
0378: JLabel lblProject = new JLabel();
0379: JLabel lblLocation = new JLabel();
0380: JLabel lblFile = new JLabel();
0381: tfClassToTest = new JTextField(25);
0382: btnBrowse = new JButton();
0383: tfTestClass = new JTextField();
0384: tfProjectName = new JTextField();
0385: cboxLocation = new JComboBox();
0386: tfCreatedFile = new JTextField();
0387:
0388: ResourceBundle bundle = NbBundle
0389: .getBundle(SimpleTestStepLocation.class);
0390:
0391: Mnemonics.setLocalizedText(lblClassToTest, bundle
0392: .getString("LBL_ClassToTest"));//NOI18N
0393: Mnemonics.setLocalizedText(lblCreatedTestClass, bundle
0394: .getString("LBL_TestClass")); //NOI18N
0395: Mnemonics.setLocalizedText(lblProject, bundle
0396: .getString("LBL_Project")); //NOI18N
0397: Mnemonics.setLocalizedText(lblLocation, bundle
0398: .getString("LBL_Location")); //NOI18N
0399: Mnemonics.setLocalizedText(lblFile, bundle
0400: .getString("LBL_CreatedFile"));//NOI18N
0401: Mnemonics.setLocalizedText(btnBrowse, bundle
0402: .getString("LBL_Browse")); //NOI18N
0403:
0404: lblClassToTest.setLabelFor(tfClassToTest);
0405: lblCreatedTestClass.setLabelFor(tfTestClass);
0406: lblProject.setLabelFor(tfProjectName);
0407: lblFile.setLabelFor(tfCreatedFile);
0408: lblLocation.setLabelFor(cboxLocation);
0409:
0410: tfTestClass.setEditable(false);
0411: tfProjectName.setEditable(false);
0412: tfCreatedFile.setEditable(false);
0413:
0414: tfTestClass.setFocusable(false);
0415: tfProjectName.setFocusable(false);
0416: tfCreatedFile.setFocusable(false);
0417:
0418: cboxLocation.setEditable(false);
0419:
0420: JCheckBox[] chkBoxes;
0421:
0422: JComponent accessLevels = GuiUtils.createChkBoxGroup(NbBundle
0423: .getMessage(GuiUtils.class,
0424: "JUnitCfgOfCreate.groupAccessLevels"), //NOI18N
0425: chkBoxes = GuiUtils.createCheckBoxes(new String[] {
0426: GuiUtils.CHK_PUBLIC, GuiUtils.CHK_PROTECTED,
0427: GuiUtils.CHK_PACKAGE }));
0428: chkPublic = chkBoxes[0];
0429: chkProtected = chkBoxes[1];
0430: chkPackagePrivate = chkBoxes[2];
0431:
0432: JComponent optCode = GuiUtils.createChkBoxGroup(NbBundle
0433: .getMessage(GuiUtils.class,
0434: "JUnitCfgOfCreate.groupOptCode"), //NOI18N
0435: chkBoxes = GuiUtils.createCheckBoxes(new String[] {
0436: GuiUtils.CHK_SETUP, GuiUtils.CHK_TEARDOWN,
0437: GuiUtils.CHK_METHOD_BODIES }));
0438: chkSetUp = chkBoxes[0];
0439: chkTearDown = chkBoxes[1];
0440: chkMethodBodies = chkBoxes[2];
0441:
0442: JComponent optComments = GuiUtils.createChkBoxGroup(NbBundle
0443: .getMessage(GuiUtils.class,
0444: "JUnitCfgOfCreate.groupOptComments"), //NOI18N
0445: chkBoxes = GuiUtils.createCheckBoxes(new String[] {
0446: GuiUtils.CHK_JAVADOC, GuiUtils.CHK_HINTS }));
0447: chkJavadoc = chkBoxes[0];
0448: chkHints = chkBoxes[1];
0449:
0450: /* set layout of the components: */
0451: JPanel targetPanel = new SizeRestrictedPanel(
0452: new GridBagLayout(), false, true);
0453:
0454: GridBagConstraints gbcLeft = new GridBagConstraints();
0455: gbcLeft.anchor = GridBagConstraints.WEST;
0456: gbcLeft.gridwidth = 1;
0457: gbcLeft.insets = new Insets(0, 0, 6, 12);
0458: gbcLeft.fill = GridBagConstraints.NONE;
0459: gbcLeft.weightx = 0.0f;
0460:
0461: GridBagConstraints gbcRight = new GridBagConstraints();
0462: gbcRight.anchor = GridBagConstraints.WEST;
0463: gbcRight.gridwidth = GridBagConstraints.REMAINDER;
0464: gbcRight.insets = new Insets(0, 0, 6, 0);
0465: gbcRight.fill = GridBagConstraints.BOTH;
0466: gbcRight.weightx = 1.0f;
0467:
0468: // Class to Test:
0469:
0470: gbcRight.gridwidth = 1;
0471:
0472: GridBagConstraints gbcBrowse = new GridBagConstraints();
0473: gbcBrowse.insets = new Insets(0, 11, 6, 0);
0474: gbcBrowse.gridwidth = GridBagConstraints.REMAINDER;
0475:
0476: targetPanel.add(lblClassToTest, gbcLeft);
0477: targetPanel.add(tfClassToTest, gbcRight);
0478: targetPanel.add(btnBrowse, gbcBrowse);
0479:
0480: // Created Test Class:
0481:
0482: gbcLeft.insets.bottom = gbcRight.insets.bottom = 24;
0483:
0484: targetPanel.add(lblCreatedTestClass, gbcLeft);
0485: targetPanel.add(tfTestClass, gbcRight);
0486: targetPanel.add(new JPanel(), gbcBrowse); //filler
0487:
0488: // Project:
0489:
0490: gbcRight.gridwidth = GridBagConstraints.REMAINDER;
0491:
0492: gbcLeft.insets.bottom = gbcRight.insets.bottom = 6;
0493:
0494: targetPanel.add(lblProject, gbcLeft);
0495: targetPanel.add(tfProjectName, gbcRight);
0496:
0497: // Location:
0498:
0499: gbcLeft.insets.bottom = gbcRight.insets.bottom = 12;
0500:
0501: targetPanel.add(lblLocation, gbcLeft);
0502: targetPanel.add(cboxLocation, gbcRight);
0503:
0504: // Created File:
0505:
0506: gbcLeft.insets.bottom = gbcRight.insets.bottom = 0;
0507:
0508: targetPanel.add(lblFile, gbcLeft);
0509: targetPanel.add(tfCreatedFile, gbcRight);
0510:
0511: JComponent optionsBox = new SizeRestrictedPanel(false, true);
0512: optionsBox
0513: .setLayout(new BoxLayout(optionsBox, BoxLayout.X_AXIS));
0514: optionsBox.add(accessLevels);
0515: optionsBox.add(Box.createHorizontalStrut(18));
0516: optionsBox.add(optCode);
0517: optionsBox.add(Box.createHorizontalStrut(18));
0518: optionsBox.add(optComments);
0519: //align groups of the checkboxes vertically to the top:
0520: accessLevels.setAlignmentY(0.0f);
0521: optCode.setAlignmentY(0.0f);
0522: optComments.setAlignmentY(0.0f);
0523:
0524: final Box result = Box.createVerticalBox();
0525: result.add(targetPanel);
0526: result.add(Box.createVerticalStrut(12));
0527: JPanel separatorPanel = new SizeRestrictedPanel(
0528: new GridLayout(), false, true);
0529: separatorPanel.add(new JSeparator());
0530: result.add(separatorPanel);
0531: result.add(Box.createVerticalStrut(12));
0532: result.add(optionsBox);
0533: //result.add(Box.createVerticalGlue()); //not necessary
0534:
0535: /* tune layout of the components within the box: */
0536: targetPanel.setAlignmentX(0.0f);
0537: optionsBox.setAlignmentX(0.0f);
0538: optCode.setAlignmentX(0.0f);
0539: optComments.setAlignmentX(0.0f);
0540:
0541: result.setName(bundle.getString("LBL_panel_ChooseClass"));
0542:
0543: addAccessibilityDescriptions(result);
0544: setUpInteraction();
0545:
0546: return result;
0547: }
0548:
0549: /**
0550: * Sets up tooltips and accessibility names and descriptions
0551: * for GUI elements of the wizard panel.
0552: *
0553: * @param wizPanel wizard panel whose elements need to be made accessible.
0554: */
0555: private void addAccessibilityDescriptions(Component wizPanel) {
0556: final ResourceBundle bundle = NbBundle
0557: .getBundle(SimpleTestStepLocation.class);
0558:
0559: tfClassToTest.setToolTipText(bundle
0560: .getString("SimpleTest.classToTest.toolTip")); //NOI18N
0561: tfClassToTest.getAccessibleContext().setAccessibleName(
0562: bundle.getString("SimpleTest.classToTest.AN")); //NOI18N
0563: tfClassToTest.getAccessibleContext().setAccessibleDescription(
0564: bundle.getString("SimpleTest.classToTest.AD")); //NOI18N
0565:
0566: btnBrowse.setToolTipText(bundle
0567: .getString("SimpleTest.btnBrowse.toolTip")); //NOI18N
0568: btnBrowse.getAccessibleContext().setAccessibleName(
0569: bundle.getString("SimpleTest.btnBrowse.AN")); //NOI18N
0570: btnBrowse.getAccessibleContext().setAccessibleDescription(
0571: bundle.getString("SimpleTest.btnBrowse.AD")); //NOI18N
0572:
0573: cboxLocation.setToolTipText(bundle
0574: .getString("SimpleTest.location.toolTip")); //NOI18N
0575: cboxLocation.getAccessibleContext().setAccessibleName(
0576: bundle.getString("SimpleTest.location.AN")); //NOI18N
0577: cboxLocation.getAccessibleContext().setAccessibleDescription(
0578: bundle.getString("SimpleTest.location.AD")); //NOI18N
0579:
0580: wizPanel.getAccessibleContext().setAccessibleDescription(
0581: bundle.getString("SimpleTest.AD")); //NOI18N
0582: }
0583:
0584: /**
0585: * <!-- PENDING -->
0586: *
0587: * @return <code>true</code> if the selected item has changed,
0588: * <code>false</code> otherwise
0589: */
0590: private boolean updateLocationComboBox() {
0591: Object[] srcRootsToOffer;
0592:
0593: if ((allTestSourceGroups.length == 1) || (srcGroup == null)) {
0594: srcRootsToOffer = allTestSourceGroups;
0595: } else {
0596: srcRootsToOffer = sourcesToTestsMap.get(srcGroup);
0597: }
0598:
0599: Object previousSelectedItem = cboxLocation.getSelectedItem();
0600:
0601: ignoreCboxItemChanges = true;
0602: try {
0603: Object[] items = createNamedItems(srcRootsToOffer);
0604: cboxLocation.setModel(new DefaultComboBoxModel(items));
0605: if (previousSelectedItem != null) {
0606: cboxLocation.setSelectedItem(previousSelectedItem);//may not process
0607: }
0608: } finally {
0609: ignoreCboxItemChanges = false;
0610: }
0611:
0612: Object newSelectedItem = cboxLocation.getSelectedItem();
0613:
0614: return !newSelectedItem.equals(previousSelectedItem);
0615: }
0616:
0617: /**
0618: */
0619: private static NamedObject[] createNamedItems(
0620: final Object[] srcRoots) {
0621:
0622: //PENDING - should not the source groups be sorted (alphabetically)?
0623: NamedObject[] items = new NamedObject[srcRoots.length];
0624: for (int i = 0; i < srcRoots.length; i++) {
0625: String name = (srcRoots[i] instanceof SourceGroup) ? ((SourceGroup) srcRoots[i])
0626: .getDisplayName()
0627: : (srcRoots[i] instanceof FileObject) ? FileUtil
0628: .getFileDisplayName((FileObject) srcRoots[i])
0629: : srcRoots[i].toString();
0630: items[i] = new NamedObject(srcRoots[i], name);
0631: }
0632: return items;
0633: }
0634:
0635: /**
0636: */
0637: private void setUpInteraction() {
0638:
0639: class UIListener implements ActionListener, DocumentListener,
0640: FocusListener, ItemListener {
0641: public void actionPerformed(ActionEvent e) {
0642:
0643: /* button Browse... pressed */
0644:
0645: chooseClass();
0646: }
0647:
0648: public void insertUpdate(DocumentEvent e) {
0649: classNameChanged();
0650: }
0651:
0652: public void removeUpdate(DocumentEvent e) {
0653: classNameChanged();
0654: }
0655:
0656: public void changedUpdate(DocumentEvent e) {
0657: classNameChanged();
0658: }
0659:
0660: public void focusGained(FocusEvent e) {
0661: Object source = e.getSource();
0662: if (source == tfClassToTest) {
0663: //tfClassToTest.getDocument().addDocumentListener(this);
0664: }
0665: }
0666:
0667: public void focusLost(FocusEvent e) {
0668: Object source = e.getSource();
0669: if (source == tfClassToTest) {
0670: //tfClassToTest.getDocument().removeDocumentListener(this);
0671: if (!e.isTemporary()) {
0672: tfClassToTestFocusLost(e);
0673: }
0674: } else if ((source == btnBrowse) && !e.isTemporary()) {
0675: btnBrowseFocusLost(e);
0676: }
0677: }
0678:
0679: public void itemStateChanged(ItemEvent e) {
0680: if (e.getSource() == cboxLocation) {
0681: if (!ignoreCboxItemChanges) {
0682: locationChanged();
0683: }
0684: } else {
0685: /*
0686: * source is one of the Method Access Levels ckeck-boxes
0687: */
0688: checkChkBoxesValidity();
0689: setValidity();
0690: }
0691: }
0692: }
0693:
0694: final UIListener listener = new UIListener();
0695:
0696: btnBrowse.addActionListener(listener);
0697: tfClassToTest.addFocusListener(listener);
0698: btnBrowse.addFocusListener(listener);
0699: cboxLocation.addItemListener(listener);
0700: chkPublic.addItemListener(listener);
0701: chkProtected.addItemListener(listener);
0702: chkPackagePrivate.addItemListener(listener);
0703: tfClassToTest.getDocument().addDocumentListener(listener);
0704: }
0705:
0706: /**
0707: */
0708: private void tfClassToTestFocusLost(FocusEvent e) {
0709: final Component allowFocusGain = focusGainAllowedFor;
0710: focusGainAllowedFor = null;
0711:
0712: if (multipleSourceRoots && interactionRestrictionsActive
0713: && !interactionRestrictionsSuspended) {
0714:
0715: final Component opposite = e.getOppositeComponent();
0716:
0717: if ((allowFocusGain != null)
0718: && (opposite == allowFocusGain)) {
0719: return;
0720: }
0721: if (opposite == btnBrowse) {
0722: return;
0723: }
0724: if ((opposite instanceof JLabel)
0725: && (((JLabel) opposite).getLabelFor() == tfClassToTest)) {
0726: /*
0727: * When a JLabel's mnemonic key is pressed, the JLabel gains focus
0728: * until the key is released again. That's why we must ignore such
0729: * focus transfers.
0730: */
0731: return;
0732: }
0733:
0734: if (!maybeDisplaySourceGroupChooser()) {
0735:
0736: /* send the request back to the Test to Class textfield: */
0737: tfClassToTest.requestFocus();
0738: }
0739: }
0740: }
0741:
0742: /**
0743: */
0744: private void btnBrowseFocusLost(FocusEvent e) {
0745: final Component allowFocusGain = focusGainAllowedFor;
0746: focusGainAllowedFor = null;
0747:
0748: if (multipleSourceRoots && interactionRestrictionsActive
0749: && !interactionRestrictionsSuspended) {
0750:
0751: final Component opposite = e.getOppositeComponent();
0752:
0753: if ((allowFocusGain != null)
0754: && (opposite == allowFocusGain)) {
0755: return;
0756: }
0757: if (opposite == tfClassToTest) {
0758: return;
0759: }
0760: if ((opposite instanceof JLabel)
0761: && (((JLabel) opposite).getLabelFor() == tfClassToTest)) {
0762: /*
0763: * When a JLabel's mnemonic key is pressed, the JLabel gains focus
0764: * until the key is released again. That's why we must ignore such
0765: * focus transfers.
0766: */
0767: return;
0768: }
0769:
0770: if (!maybeDisplaySourceGroupChooser()) {
0771:
0772: /* send the request back to the Browse... button: */
0773: btnBrowse.requestFocus();
0774: }
0775: }
0776: }
0777:
0778: /**
0779: * <!-- PENDING -->
0780: *
0781: * @return <code>false</code> if the SourceGroup chooser was displayed
0782: * and the user cancelled the choice; <code>true</code> otherwise
0783: */
0784: private boolean maybeDisplaySourceGroupChooser() {
0785: assert multipleSourceRoots;
0786:
0787: if (classExists && (srcGroup == null)) {
0788: SourceGroup[] candidates = findParentGroupCandidates();
0789:
0790: assert candidates.length != 0; //because the class exists
0791:
0792: if (candidates.length == 1) {
0793: setSelectedSrcGroup(candidates[0]);
0794: return true;
0795: } else {
0796: SourceGroup chosenSrcGroup = chooseSrcGroup(candidates);
0797: if (chosenSrcGroup != null) {
0798: setSelectedSrcGroup(chosenSrcGroup);
0799: return true;
0800: } else {
0801: return false;
0802: }
0803: }
0804: } else {
0805: return true;
0806: }
0807: }
0808:
0809: /**
0810: * Displays a source root chooser which allows the user to choose
0811: * a parent source root for the entered class name.
0812: *
0813: * @param candidates source roots to be offered to the user
0814: * @return the chosen source root,
0815: * or <code>null</code> if the user cancelled the choice
0816: */
0817: private SourceGroup chooseSrcGroup(final SourceGroup[] candidates) {
0818: assert (candidates != null) && (candidates.length != 0);
0819:
0820: final String[] rootNames = new String[candidates.length];
0821: for (int i = 0; i < rootNames.length; i++) {
0822: rootNames[i] = candidates[i].getDisplayName();
0823: }
0824:
0825: final JButton btn = new JButton(NbBundle.getMessage(getClass(),
0826: "LBL_SelectBtn")); //NOI18N
0827: final JList list = new JList(rootNames);
0828: list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
0829: list.setSelectedIndex(0);
0830: list.addListSelectionListener(new ListSelectionListener() {
0831: public void valueChanged(ListSelectionEvent e) {
0832: btn.setEnabled(!list.isSelectionEmpty());
0833: }
0834: });
0835: JPanel panel = new JPanel(new BorderLayout(0, 0));
0836: panel.add(list, BorderLayout.CENTER);
0837: panel.setBorder(BorderFactory.createCompoundBorder(
0838: BorderFactory.createEmptyBorder(12, 12, 0, 12),
0839: BorderFactory.createBevelBorder(BevelBorder.LOWERED)));
0840:
0841: String dialogTitle = NbBundle.getMessage(getClass(),
0842: "LBL_SourceRootChooserTitle"); //NOI18N
0843: DialogDescriptor descriptor = new DialogDescriptor(
0844: panel, //component
0845: dialogTitle, //title
0846: true, //modal
0847: new Object[] { //options
0848: btn, NotifyDescriptor.CANCEL_OPTION },
0849: btn, //default option
0850: DialogDescriptor.DEFAULT_ALIGN, (HelpCtx) null,
0851: (ActionListener) null);
0852: Object selected = DialogDisplayer.getDefault().notify(
0853: descriptor);
0854: return (selected == btn) ? candidates[list.getSelectedIndex()]
0855: : (SourceGroup) null;
0856: }
0857:
0858: /**
0859: */
0860: private void setSelectedSrcGroup(SourceGroup srcGroup) {
0861: setSelectedSrcGroup(srcGroup, true);
0862: }
0863:
0864: /**
0865: * <!-- PENDING -->
0866: */
0867: private void setSelectedSrcGroup(SourceGroup srcGroup,
0868: boolean updateDisp) {
0869: assert multipleSourceRoots
0870: && ((srcGroup == null) || (classNameValid && classExists));
0871:
0872: if (!checkObjChanged(this .srcGroup, srcGroup)) {
0873: return;
0874: }
0875:
0876: this .srcGroup = srcGroup;
0877:
0878: if (updateDisp) {
0879:
0880: /* update the display: */
0881: try {
0882: programmaticChange = true;
0883:
0884: String className = tfClassToTest.getText().substring(0,
0885: classNameLength);
0886: String srcGroupDisplay = getSrcGrpDisp(srcGroup);
0887:
0888: ignoreClsNameChanges = true;
0889: tfClassToTest.setText(className + srcGroupDisplay);
0890: ignoreClsNameChanges = false;
0891:
0892: classNameLength = className.length();
0893: classNameChanged();
0894: srcGroupNameDisplayed = true;
0895: setNavigationFilterEnabled(true);
0896: } finally {
0897: ignoreClsNameChanges = false;
0898: programmaticChange = false;
0899: }
0900: }
0901:
0902: updateInteractionRestrictionsState();
0903:
0904: /*
0905: * There is no need to check and set validity.
0906: * The user should be offered to choose a source root only when
0907: * the entered class name is valid and the class exists
0908: * in at least two source roots.
0909: */
0910:
0911: /* update target folder: */
0912: if (allTestSourceGroups.length > 1) {
0913: boolean locationChanged = updateLocationComboBox();
0914: if (locationChanged) {
0915: updateTargetFolderData();
0916: }
0917: }
0918:
0919: /* update name of the file to be created: */
0920: updateCreatedFileName();
0921:
0922: /* set 'srcFile': */
0923: srcFile = (srcGroup != null) ? srcGroup.getRootFolder()
0924: .getFileObject(srcRelFileNameSys) : null;
0925:
0926: assert (srcGroup == null) || (srcFile != null);
0927: }
0928:
0929: /**
0930: */
0931: private static String getSrcGrpDisp(SourceGroup srcGroup) {
0932: if (srcGroup == null) {
0933: return ""; //NOI18N
0934: } else {
0935: String srcGroupName = srcGroup.getDisplayName();
0936: return new StringBuffer(srcGroupName.length() + 3).append(
0937: ' ').append('(').append(srcGroupName).append(')')
0938: .toString();
0939: }
0940: }
0941:
0942: /**
0943: */
0944: private void setNavigationFilterEnabled(boolean enabled) {
0945: if (enabled == navigationFilterEnabled) {
0946: if (enabled) {
0947: clsNameNavigationFilter.ensureCursorInRange();
0948: }
0949: return;
0950: }
0951:
0952: if (enabled) {
0953: if (clsNameNavigationFilter == null) {
0954: clsNameNavigationFilter = new ClsNameNavigationFilter();
0955: }
0956: tfClassToTest.setNavigationFilter(clsNameNavigationFilter);
0957: clsNameNavigationFilter.ensureCursorInRange();
0958: } else {
0959: tfClassToTest.setNavigationFilter(null);
0960: }
0961: this .navigationFilterEnabled = enabled;
0962: }
0963:
0964: /**
0965: * <!-- PENDING -->
0966: */
0967: private void updateInteractionRestrictionsState() {
0968: setInteractionRestrictionsSuspended(!classNameValid
0969: || !classExists || (srcGroup != null));
0970: }
0971:
0972: /**
0973: */
0974: private void updateTargetFolderData() {
0975: Object item = cboxLocation.getSelectedItem();
0976: if (item != null) {
0977: SourceGroup targetSourceGroup = (SourceGroup) ((NamedObject) item).object;
0978: testRootFolder = targetSourceGroup.getRootFolder();
0979: testsRootDirName = FileUtil
0980: .getFileDisplayName(testRootFolder);
0981: } else {
0982: testRootFolder = null;
0983: testsRootDirName = ""; //NOI18N
0984: }
0985: }
0986:
0987: /**
0988: * Called whenever selection in the Location combo-box is changed.
0989: */
0990: private void locationChanged() {
0991: updateTargetFolderData();
0992: updateCreatedFileName();
0993: }
0994:
0995: /**
0996: */
0997: private void classNameChanged() {
0998: if (ignoreClsNameChanges) {
0999: return;
1000: }
1001:
1002: String className;
1003: if (!programmaticChange) {
1004: className = tfClassToTest.getText().trim();
1005: classNameLength = className.length();
1006: } else {
1007: className = tfClassToTest.getText().substring(0,
1008: classNameLength);
1009: }
1010:
1011: String testClassName;
1012: if (className.length() != 0) {
1013: srcRelFileNameSys = className.replace('.', '/') + ".java"; //NOI18N
1014: testClassName = className + testClassNameSuffix;
1015: testRelFileName = testClassName.replace('.',
1016: File.separatorChar)
1017: + ".java"; //NOI18N
1018: } else {
1019: srcRelFileNameSys = ""; //NOI18N
1020: testClassName = ""; //NOI18N
1021: testRelFileName = ""; //NOI18N
1022: }
1023: tfTestClass.setText(testClassName);
1024:
1025: if (!programmaticChange) {
1026: updateCreatedFileName();
1027: if (checkClassNameValidity()) {
1028: checkSelectedClassExists();
1029: }
1030: setErrorMsg(msgStack.getDisplayedMessage());
1031: setValidity();
1032:
1033: /*
1034: * The user modified the class name.
1035: * It may be ambiguous - it may match classes in multiple SourceGroups.
1036: */
1037: if (multipleSourceRoots) {
1038: setSelectedSrcGroup(null, false);
1039: }
1040: }
1041:
1042: if (multipleSourceRoots) {
1043: updateInteractionRestrictionsState();
1044: }
1045: }
1046:
1047: /**
1048: * Identifies all <code>SourceGroup</code>s containing file having the
1049: * name entered by the user.
1050: * This method assumes that at least one such <code>SourceGroup</code>
1051: * has already been found and its index stored in field
1052: * {@link #sourceGroupParentIndex}.
1053: *
1054: * @return array of matching <code>SourceGroup</code>s
1055: * (always contains at least one element)
1056: */
1057: private SourceGroup[] findParentGroupCandidates() {
1058: assert sourceGroupParentIndex >= 0;
1059:
1060: List<SourceGroup> cands = null;
1061: final int count = testableSourceGroups.length;
1062: for (int i = sourceGroupParentIndex + 1; i < count; i++) {
1063: final FileObject groupRoot = testableSourceGroupsRoots[i];
1064: FileObject srcFile = groupRoot
1065: .getFileObject(srcRelFileNameSys);
1066: if (srcFile != null
1067: && testableSourceGroups[i].contains(srcFile)) {
1068: if (cands == null) {
1069: cands = new ArrayList<SourceGroup>(
1070: testableSourceGroups.length - i + 1);
1071: cands
1072: .add(testableSourceGroups[sourceGroupParentIndex]);
1073: }
1074: cands.add(testableSourceGroups[i]);
1075: }
1076: }
1077: return cands == null ? new SourceGroup[] { testableSourceGroups[sourceGroupParentIndex] }
1078: : cands.toArray(new SourceGroup[cands.size()]);
1079: }
1080:
1081: /**
1082: */
1083: private void updateCreatedFileName() {
1084: tfCreatedFile.setText(testsRootDirName + File.separatorChar
1085: + testRelFileName);
1086: }
1087:
1088: /**
1089: * Checks validity of the entered class name, updates messages
1090: * on the message stack and updates the <code>classNameValid</code> field.
1091: *
1092: * @see #msgStack
1093: * @see #setValidity()
1094: */
1095: private boolean checkClassNameValidity() {
1096: String className = tfClassToTest.getText().trim();
1097: if (srcGroupNameDisplayed) {
1098: className = className.substring(0, classNameLength);
1099: }
1100:
1101: if (className.length() == 0) {
1102: msgStack.clearMessage(MSG_LAYER_CLASSNAME);
1103: classNameValid = false;
1104: } else if (Utils.isValidClassName(className)) {
1105: msgStack.clearMessage(MSG_LAYER_CLASSNAME);
1106: classNameValid = true;
1107: } else {
1108: if (msgClassNameInvalid == null) {
1109: msgClassNameInvalid = NbBundle.getMessage(
1110: JUnitCfgOfCreate.class, "MSG_InvalidClassName"); //NOI18N
1111: }
1112: msgStack.setMessage(MSG_LAYER_CLASSNAME,
1113: msgClassNameInvalid);
1114: classNameValid = false;
1115: }
1116:
1117: return classNameValid;
1118: }
1119:
1120: /**
1121: * Checks whether a class having the entered name exists, updates messages
1122: * on the message stack and updates the <code>classExists</code> field.
1123: *
1124: * @see #setValidity()
1125: */
1126: private boolean checkSelectedClassExists() {
1127: sourceGroupParentIndex = -1;
1128:
1129: final int count = testableSourceGroups.length;
1130: for (int i = 0; i < count; i++) {
1131: final FileObject groupRoot = testableSourceGroupsRoots[i];
1132: FileObject srcFile = groupRoot
1133: .getFileObject(srcRelFileNameSys);
1134: if (srcFile != null
1135: && testableSourceGroups[i].contains(srcFile)) {
1136: this .srcFile = srcFile;
1137: sourceGroupParentIndex = i;
1138: break;
1139: }
1140: }
1141:
1142: classExists = (sourceGroupParentIndex != -1);
1143:
1144: if (classExists) {
1145: msgStack.clearMessage(MSG_LAYER_CLASSNAME);
1146: } else {
1147: if (msgClassToTestDoesNotExist == null) {
1148: msgClassToTestDoesNotExist = NbBundle.getMessage(
1149: SimpleTestStepLocation.class,
1150: "MSG_ClassToTestDoesNotExist"); //NOI18N
1151: }
1152: msgStack.setMessage(MSG_LAYER_CLASSNAME,
1153: msgClassToTestDoesNotExist);
1154: }
1155:
1156: return classExists;
1157: }
1158:
1159: /**
1160: * Checks whether at least one of the <em>Method Access Levels</em>
1161: * checkboxes is selected, updates messages
1162: * on the message stack and updates the <code>chkBoxesValid</code> field.
1163: *
1164: * @see #setValidity()
1165: */
1166: private boolean checkChkBoxesValidity() {
1167: chkBoxesValid = chkPublic.isSelected()
1168: || chkProtected.isSelected()
1169: || chkPackagePrivate.isSelected();
1170: String msgUpdate;
1171: if (chkBoxesValid) {
1172: msgUpdate = msgStack.clearMessage(MSG_LAYER_CHECKBOXES);
1173: } else {
1174: if (msgChkBoxesInvalid == null) {
1175: //PENDING - text of the message:
1176: msgChkBoxesInvalid = NbBundle.getMessage(
1177: JUnitCfgOfCreate.class,
1178: "MSG_AllMethodTypesDisabled"); //NOI18N
1179: }
1180: msgUpdate = msgStack.setMessage(MSG_LAYER_CHECKBOXES,
1181: msgChkBoxesInvalid);
1182: }
1183: if (msgUpdate != null) {
1184: setErrorMsg(msgStack.getDisplayedMessage());
1185: }
1186: return chkBoxesValid;
1187: }
1188:
1189: /**
1190: * Updates the <code>isValid</code> field and notifies all registered
1191: * <code>ChangeListener</code>s if validity has changed.
1192: */
1193: private void setValidity() {
1194: boolean wasValid = isValid;
1195:
1196: isValid = classNameValid && classExists && chkBoxesValid;
1197:
1198: if (isValid != wasValid) {
1199: fireChange();
1200:
1201: updateInteractionRestrictionsState();
1202:
1203: /*
1204: * This must be called after fireChange() because fireChange()
1205: * sets state (enabled/disabled) of the default button.
1206: */
1207: if (isValid && interactionRestrictionsEnabled
1208: && !interactionRestrictionsActive) {
1209: tryActivateInteractionRestrictions();
1210: }
1211: }
1212: }
1213:
1214: /**
1215: * Displays the given message in the wizard's message area.
1216: *
1217: * @param message message to be displayed, or <code>null</code>
1218: * if the message area should be cleared
1219: */
1220: private void setErrorMsg(String message) {
1221: if (wizard != null) {
1222: wizard.putProperty("WizardPanel_errorMessage", message); //NOI18N
1223: }
1224: }
1225:
1226: /**
1227: * Displays a class chooser dialog and lets the user to select a class.
1228: * If the user confirms their choice, full name of the selected class
1229: * is put into the <em>Class To Test</em> text field.
1230: */
1231: private void chooseClass() {
1232: try {
1233: final Node[] sourceGroupNodes = new Node[testableSourceGroups.length];
1234: for (int i = 0; i < sourceGroupNodes.length; i++) {
1235: /*
1236: * Note:
1237: * Precise structure of this view is *not* specified by the API.
1238: */
1239: Node srcGroupNode = PackageView
1240: .createPackageView(testableSourceGroups[i]);
1241: sourceGroupNodes[i] = new FilterNode(srcGroupNode,
1242: new JavaChildren(srcGroupNode));
1243: }
1244:
1245: Node rootNode;
1246: if (sourceGroupNodes.length == 1) {
1247: rootNode = new FilterNode(sourceGroupNodes[0],
1248: new JavaChildren(sourceGroupNodes[0]));
1249: } else {
1250: Children children = new Children.Array();
1251: children.add(sourceGroupNodes);
1252:
1253: AbstractNode node = new AbstractNode(children);
1254: node.setName("Project Source Roots"); //NOI18N
1255: node.setDisplayName(NbBundle.getMessage(getClass(),
1256: "LBL_Sources"));//NOI18N
1257: //PENDING - set a better icon for the root node
1258: rootNode = node;
1259: }
1260:
1261: NodeAcceptor acceptor = new NodeAcceptor() {
1262: public boolean acceptNodes(Node[] nodes) {
1263: Node.Cookie cookie;
1264: return nodes.length == 1
1265: && (cookie = nodes[0]
1266: .getCookie(DataObject.class)) != null
1267: && ((DataObject) cookie).getPrimaryFile()
1268: .isFolder() == false;
1269: }
1270: };
1271:
1272: Node selectedNode = NodeOperation.getDefault().select(
1273: NbBundle.getMessage(SimpleTestStepLocation.class,
1274: "LBL_WinTitle_SelectClass"), //NOI18N
1275: NbBundle.getMessage(SimpleTestStepLocation.class,
1276: "LBL_SelectClassToTest"), //NOI18N
1277: rootNode, acceptor)[0];
1278:
1279: SourceGroup selectedSourceGroup;
1280: if (sourceGroupNodes.length == 1) {
1281: selectedSourceGroup = testableSourceGroups[0];
1282: } else {
1283: Node previous = null;
1284: Node current = selectedNode.getParentNode();
1285: Node parent;
1286: while ((parent = current.getParentNode()) != null) {
1287: previous = current;
1288: current = parent;
1289: }
1290: /*
1291: * 'current' now contains the root node of displayed node
1292: * hierarchy. 'current' contains a parent node of the source
1293: * root and 'previous' contains the parent source root of
1294: * the selected class.
1295: */
1296: selectedSourceGroup = null;
1297: Node selectedSrcGroupNode = previous;
1298: for (int i = 0; i < sourceGroupNodes.length; i++) {
1299: if (sourceGroupNodes[i] == selectedSrcGroupNode) {
1300: selectedSourceGroup = testableSourceGroups[i];
1301: sourceGroupParentIndex = i;
1302: break;
1303: }
1304: }
1305: assert selectedSourceGroup != null;
1306: assert sourceGroupParentIndex >= 0;
1307: }
1308: srcGroup = selectedSourceGroup;
1309:
1310: FileObject selectedFileObj = selectedNode.getCookie(
1311: DataObject.class).getPrimaryFile();
1312:
1313: /* display selected class name: */
1314: try {
1315: programmaticChange = true;
1316:
1317: String className = getClassName(selectedFileObj);
1318: classNameLength = className.length();
1319: if (!multipleSourceRoots) {
1320: /*
1321: * Caution! Calling setText("className") triggers two
1322: * text change events - once when the original text is
1323: * cleared and the second time when the new text is set.
1324: * Method classNameChanged() must only be called when the
1325: * text change is complete (see issue #91794) so we set
1326: * the 'ignoreClsNameChanges' flag for the time the text
1327: * is being changed and then call the classNameChanged()
1328: * explicitely.
1329: */
1330: ignoreClsNameChanges = true;
1331: tfClassToTest.setText(className);
1332: ignoreClsNameChanges = false;
1333: classNameChanged();
1334: } else {
1335: String srcGroupDisplay = getSrcGrpDisp(selectedSourceGroup);
1336:
1337: ignoreClsNameChanges = true;
1338: tfClassToTest.setText(className + srcGroupDisplay);
1339: ignoreClsNameChanges = false;
1340:
1341: classNameLength = className.length();
1342: classNameChanged();
1343: srcGroupNameDisplayed = true;
1344: setNavigationFilterEnabled(true);
1345: }
1346: /*
1347: * Change of text of the Class to Test text-field triggers
1348: * update of variable 'testRelFileName'.
1349: */
1350: } finally {
1351: ignoreClsNameChanges = false;
1352: programmaticChange = false;
1353: }
1354:
1355: /* set class name validity: */
1356: classNameValid = true;
1357: classExists = true;
1358: String msgUpdate = msgStack
1359: .clearMessage(MSG_LAYER_CLASSNAME);
1360: if (msgUpdate != null) {
1361: setErrorMsg(msgUpdate);
1362: }
1363: setValidity();
1364: updateInteractionRestrictionsState();
1365:
1366: /* update target folder: */
1367: if (multipleSourceRoots && (allTestSourceGroups.length > 1)) {
1368: boolean locationChanged = updateLocationComboBox();
1369: if (locationChanged) {
1370: updateTargetFolderData(); //sets also 'testRootFolder'
1371: }
1372: }
1373:
1374: /* update name of the file to be created: */
1375: updateCreatedFileName();
1376:
1377: /* set 'srcFile': */
1378: srcFile = selectedFileObj;
1379:
1380: } catch (UserCancelException ex) {
1381: // if the user cancels the choice, do nothing
1382: }
1383: }
1384:
1385: private static String getClassName(FileObject fileObj) {
1386: //PENDING: is it ensured that the classpath is non-null?
1387: return ClassPath.getClassPath(fileObj, ClassPath.SOURCE)
1388: .getResourceName(fileObj, '.', false);
1389: }
1390:
1391: public Component getComponent() {
1392: return visualComp;
1393: }
1394:
1395: public boolean isValid() {
1396: return isValid;
1397: }
1398:
1399: public HelpCtx getHelp() {
1400: //PENDINGg
1401: return null;
1402: }
1403:
1404: public void readSettings(WizardDescriptor settings) {
1405: wizard = settings;
1406:
1407: chkPublic.setSelected(Boolean.TRUE.equals(wizard
1408: .getProperty(GuiUtils.CHK_PUBLIC)));
1409: chkProtected.setSelected(Boolean.TRUE.equals(wizard
1410: .getProperty(GuiUtils.CHK_PROTECTED)));
1411: chkPackagePrivate.setSelected(Boolean.TRUE.equals(wizard
1412: .getProperty(GuiUtils.CHK_PACKAGE)));
1413: chkSetUp.setSelected(Boolean.TRUE.equals(wizard
1414: .getProperty(GuiUtils.CHK_SETUP)));
1415: chkTearDown.setSelected(Boolean.TRUE.equals(wizard
1416: .getProperty(GuiUtils.CHK_TEARDOWN)));
1417: chkMethodBodies.setSelected(Boolean.TRUE.equals(wizard
1418: .getProperty(GuiUtils.CHK_METHOD_BODIES)));
1419: chkJavadoc.setSelected(Boolean.TRUE.equals(wizard
1420: .getProperty(GuiUtils.CHK_JAVADOC)));
1421: chkHints.setSelected(Boolean.TRUE.equals(wizard
1422: .getProperty(GuiUtils.CHK_HINTS)));
1423: }
1424:
1425: public void storeSettings(WizardDescriptor settings) {
1426: wizard = settings;
1427:
1428: wizard.putProperty(SimpleTestCaseWizard.PROP_CLASS_TO_TEST,
1429: srcFile);
1430: wizard.putProperty(SimpleTestCaseWizard.PROP_TEST_ROOT_FOLDER,
1431: testRootFolder);
1432: wizard.putProperty(GuiUtils.CHK_PUBLIC, Boolean
1433: .valueOf(chkPublic.isSelected()));
1434: wizard.putProperty(GuiUtils.CHK_PROTECTED, Boolean
1435: .valueOf(chkProtected.isSelected()));
1436: wizard.putProperty(GuiUtils.CHK_PACKAGE, Boolean
1437: .valueOf(chkPackagePrivate.isSelected()));
1438: wizard.putProperty(GuiUtils.CHK_SETUP, Boolean.valueOf(chkSetUp
1439: .isSelected()));
1440: wizard.putProperty(GuiUtils.CHK_TEARDOWN, Boolean
1441: .valueOf(chkTearDown.isSelected()));
1442: wizard.putProperty(GuiUtils.CHK_METHOD_BODIES, Boolean
1443: .valueOf(chkMethodBodies.isSelected()));
1444: wizard.putProperty(GuiUtils.CHK_JAVADOC, Boolean
1445: .valueOf(chkJavadoc.isSelected()));
1446: wizard.putProperty(GuiUtils.CHK_HINTS, Boolean.valueOf(chkHints
1447: .isSelected()));
1448: }
1449:
1450: public void addChangeListener(ChangeListener l) {
1451: if (changeListeners == null) {
1452: changeListeners = new ArrayList<ChangeListener>(4);
1453: }
1454: changeListeners.add(l);
1455: }
1456:
1457: public void removeChangeListener(ChangeListener l) {
1458: if (changeListeners != null) {
1459: if (changeListeners.remove(l) && changeListeners.isEmpty()) {
1460: changeListeners = null;
1461: }
1462: }
1463: }
1464:
1465: private void fireChange() {
1466: if (changeListeners != null) {
1467: ChangeEvent e = new ChangeEvent(this );
1468: for (ChangeListener l : changeListeners) {
1469: l.stateChanged(e);
1470: }
1471: }
1472: }
1473:
1474: /**
1475: */
1476: void setUp(final Utils utils) {
1477: final Project project = utils.getProject();
1478:
1479: if (project == this .project) {
1480: return;
1481: }
1482:
1483: this .project = project;
1484: this .sourcesToTestsMap = utils.getSourcesToTestsMap(true);
1485:
1486: int sourceGroupsCnt = sourcesToTestsMap.size();
1487: Set<Map.Entry<SourceGroup, Object[]>> mapEntries = sourcesToTestsMap
1488: .entrySet();
1489: List<SourceGroup> testGroups = new ArrayList<SourceGroup>(
1490: sourceGroupsCnt + 4);
1491:
1492: testableSourceGroups = new SourceGroup[sourceGroupsCnt];
1493: testableSourceGroupsRoots = new FileObject[sourceGroupsCnt];
1494: multipleSourceRoots = (sourceGroupsCnt > 1);
1495:
1496: Iterator<Map.Entry<SourceGroup, Object[]>> iterator = mapEntries
1497: .iterator();
1498: for (int i = 0; i < sourceGroupsCnt; i++) {
1499: Map.Entry<SourceGroup, Object[]> entry = iterator.next();
1500: SourceGroup srcGroup = entry.getKey();
1501:
1502: testableSourceGroups[i] = srcGroup;
1503: testableSourceGroupsRoots[i] = srcGroup.getRootFolder();
1504:
1505: Object[] testGroupsSubset = entry.getValue();
1506: for (int j = 0; j < testGroupsSubset.length; j++) {
1507: SourceGroup testGroup = (SourceGroup) testGroupsSubset[j];
1508: if (!testGroups.contains(testGroup)) {
1509: testGroups.add(testGroup);
1510: }
1511: }
1512: }
1513: allTestSourceGroups = testGroups
1514: .toArray(new SourceGroup[testGroups.size()]);
1515:
1516: tfProjectName.setText(ProjectUtils.getInformation(project)
1517: .getDisplayName());
1518: try {
1519: programmaticChange = true;
1520:
1521: ignoreClsNameChanges = true;
1522: tfClassToTest.setText(""); //NOI18N
1523: ignoreClsNameChanges = false;
1524:
1525: classNameLength = 0;
1526: classNameChanged();
1527: srcGroupNameDisplayed = false;
1528: setNavigationFilterEnabled(false);
1529: } finally {
1530: ignoreClsNameChanges = false;
1531: programmaticChange = false;
1532: }
1533: if (checkClassNameValidity()) {
1534: checkSelectedClassExists();
1535: } else {
1536: classExists = false;
1537: }
1538: setErrorMsg(msgStack.getDisplayedMessage());
1539: setValidity();
1540:
1541: //PENDING - if possible, we should pre-set the test source group
1542: // corresponding to the currently selected node
1543: updateLocationComboBox();
1544: updateTargetFolderData(); //sets also 'testRootFolder'
1545: updateCreatedFileName();
1546:
1547: srcFile = null;
1548:
1549: if (!multipleSourceRoots) {
1550: setInteractionRestrictionsEnabled(false);
1551: } else {
1552: AbstractDocument doc = (AbstractDocument) tfClassToTest
1553: .getDocument();
1554: if (clsNameDocumentFilter == null) {
1555: clsNameDocumentFilter = new ClsNameDocumentFilter();
1556: }
1557: if (doc.getDocumentFilter() != clsNameDocumentFilter) {
1558: doc.setDocumentFilter(clsNameDocumentFilter);
1559: }
1560: setInteractionRestrictionsEnabled(true);
1561: }
1562: }
1563:
1564: /**
1565: */
1566: void cleanUp() {
1567: setInteractionRestrictionsEnabled(false);
1568: }
1569:
1570: /**
1571: * <!-- PENDING -->
1572: */
1573: private void setInteractionRestrictionsEnabled(boolean enabled) {
1574: if (enabled == interactionRestrictionsEnabled) {
1575: return;
1576: }
1577:
1578: class DisplayabilityListener implements HierarchyListener {
1579: public void hierarchyChanged(HierarchyEvent e) {
1580: long flags = e.getChangeFlags();
1581: if ((flags & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
1582: if (visualComp.isDisplayable()) {
1583: if (interactionRestrictionsEnabled) {
1584: setInteractionRestrictionsActive(true);
1585: }
1586: } else {
1587: setInteractionRestrictionsActive(false);
1588: }
1589: }
1590: }
1591: }
1592:
1593: if (enabled) {
1594: this .interactionRestrictionsEnabled = true;
1595:
1596: assert displayabilityListener == null;
1597: displayabilityListener = new DisplayabilityListener();
1598: visualComp.addHierarchyListener(displayabilityListener);
1599:
1600: if (visualComp.isDisplayable()) {
1601: setInteractionRestrictionsActive(true);
1602: }
1603: } else {
1604: this .interactionRestrictionsEnabled = false;
1605:
1606: setInteractionRestrictionsActive(false);
1607:
1608: visualComp.removeHierarchyListener(displayabilityListener);
1609: displayabilityListener = null;
1610: }
1611: }
1612:
1613: /**
1614: * Activates or deactivates the focus detection mechanism.
1615: * <!-- PENDING -->
1616: */
1617: private void setInteractionRestrictionsActive(boolean active) {
1618: if (active == this .interactionRestrictionsActive) {
1619: return;
1620: }
1621:
1622: if (active) {
1623: tryActivateInteractionRestrictions();
1624: } else {
1625: deactivateInteractionRestrictions();
1626: }
1627: }
1628:
1629: /**
1630: */
1631: private void tryActivateInteractionRestrictions() {
1632: assert interactionRestrictionsActive == false;
1633: assert interactionRestrictionsEnabled;
1634:
1635: if (rootPane == null) {
1636: rootPane = SwingUtilities.getRootPane(visualComp);
1637: }
1638:
1639: if (rootPane != null) {
1640: defaultButton = rootPane.getDefaultButton();
1641: if (defaultButton != null) {
1642: activateInteractionRestrictions();
1643: }
1644: }
1645: }
1646:
1647: /**
1648: */
1649: private void activateInteractionRestrictions() {
1650: assert interactionRestrictionsActive == false;
1651: assert (rootPane != null) && (defaultButton != null);
1652:
1653: if ((mouseBlocked == null) || (mnemonicBlocked == null)) {
1654: findComponentsToBlock();
1655: assert (mouseBlocked != null) && (mnemonicBlocked != null);
1656: }
1657: blockDefaultRootPaneAction();
1658: blockMnemonics();
1659: setMouseClicksBlockingActive(!interactionRestrictionsSuspended);
1660:
1661: interactionRestrictionsActive = true;
1662: }
1663:
1664: /**
1665: */
1666: private void deactivateInteractionRestrictions() {
1667: assert interactionRestrictionsActive == true;
1668: assert (defaultButton != null) && (rootPane != null);
1669:
1670: setMouseClicksBlockingActive(false);
1671: unblockMnemonics();
1672: unblockDefaultRootPaneAction();
1673:
1674: defaultButton = null;
1675: rootPane = null;
1676:
1677: interactionRestrictionsActive = false;
1678: interactionRestrictionsSuspended = false;
1679: }
1680:
1681: /**
1682: */
1683: private void setInteractionRestrictionsSuspended(boolean suspended) {
1684: if (suspended != this .interactionRestrictionsSuspended) {
1685: setMouseClicksBlockingActive(interactionRestrictionsActive
1686: && !suspended);
1687: this .interactionRestrictionsSuspended = suspended;
1688: }
1689: }
1690:
1691: /**
1692: */
1693: private void setMouseClicksBlockingActive(boolean blockingActive) {
1694: if (blockingActive != this .mouseClicksBlocked) {
1695: if (blockingActive) {
1696: blockMouseClicks();
1697: } else {
1698: unblockMouseClicks();
1699: }
1700: this .mouseClicksBlocked = blockingActive;
1701: }
1702: }
1703:
1704: /**
1705: * Searches the visual component and collects components
1706: * on which mouse events or activation by mnemonics needs to be check
1707: * and evaluated.
1708: *
1709: * @see #mouseBlocked
1710: * @see #mnemonicBlocked
1711: */
1712: private void findComponentsToBlock() {
1713: assert rootPane != null;
1714:
1715: final Collection<Component> mouseBlocked = new ArrayList<Component>(
1716: 20);
1717: final Collection<JComponent> mnemBlocked = new ArrayList<JComponent>(
1718: 20);
1719:
1720: final List<Component> stack = new ArrayList<Component>(16);
1721: stack.add(rootPane.getContentPane());
1722: int lastIndex = 0;
1723:
1724: while (lastIndex != -1) {
1725:
1726: Component c = stack.remove(lastIndex--);
1727:
1728: if (!c.isVisible()) {
1729: continue;
1730: }
1731:
1732: if (c instanceof JLabel) {
1733: JLabel lbl = (JLabel) c;
1734: Component labelFor = lbl.getLabelFor();
1735: if ((labelFor != null) && (labelFor != tfClassToTest)
1736: && (lbl.getDisplayedMnemonic() != 0)) {
1737: mnemBlocked.add(lbl);
1738: }
1739: } else if (c instanceof AbstractButton) {
1740: if (c != btnBrowse) {
1741: AbstractButton btn = (AbstractButton) c;
1742: mouseBlocked.add(btn);
1743: if (btn.getMnemonic() != 0) {
1744: mnemBlocked.add(btn);
1745: }
1746: }
1747: } else if (!(c instanceof Container)) {
1748: if (c.isFocusable() && (c != tfClassToTest)) {
1749: mouseBlocked.add(c);
1750: }
1751: } else {
1752: Component[] content = ((Container) c).getComponents();
1753: switch (content.length) {
1754: case 0:
1755: break;
1756: case 1:
1757: stack.add(content[0]);
1758: lastIndex++;
1759: break;
1760: default:
1761: stack.addAll(Arrays.asList(content));
1762: lastIndex += content.length;
1763: break;
1764: }
1765: }
1766: }
1767: //mouseBlocked.add(defaultButton);
1768: //mnemBlocked.add(defaultButton);
1769:
1770: this .mouseBlocked = new Component[mouseBlocked.size()];
1771: if (mouseBlocked.size() != 0) {
1772: mouseBlocked.toArray(this .mouseBlocked);
1773: }
1774: this .mnemonicBlocked = new JComponent[mnemBlocked.size()];
1775: if (mnemBlocked.size() != 0) {
1776: mnemBlocked.toArray(this .mnemonicBlocked);
1777: }
1778: }
1779:
1780: /**
1781: */
1782: private void blockDefaultRootPaneAction() {
1783: assert (rootPane != null) && (defaultButton != null)
1784: && (rootPane.getDefaultButton() == defaultButton);
1785:
1786: final String actionKey1 = "press"; //NOI18N
1787: final String actionKey2 = "pressed"; //NOI18N
1788: String actionKey;
1789:
1790: ActionMap actionMap = rootPane.getActionMap();
1791:
1792: Action originalAction = actionMap.get(actionKey = actionKey1);
1793: if (originalAction == null) {
1794: originalAction = actionMap.get(actionKey = actionKey2);
1795: }
1796: assert originalAction != null;
1797:
1798: if (originalAction == null) {
1799: return;
1800: }
1801:
1802: actionMap.put(actionKey, new SelectSrcGrpAction(rootPane,
1803: originalAction));
1804: rootPaneDefaultActionKey = actionKey;
1805: rootPaneDefaultAction = originalAction;
1806: }
1807:
1808: /**
1809: */
1810: private void unblockDefaultRootPaneAction() {
1811: assert rootPane != null;
1812:
1813: if (rootPaneDefaultAction == null) {
1814:
1815: /* blockDefaultRootPaneAction() did not pass */
1816: return;
1817: }
1818:
1819: rootPane.getActionMap().put(rootPaneDefaultActionKey,
1820: rootPaneDefaultAction);
1821:
1822: rootPaneDefaultActionKey = null;
1823: rootPaneDefaultAction = null;
1824: }
1825:
1826: /**
1827: * Modifies behaviour of the default button.
1828: */
1829: private void blockMnemonics() {
1830: assert rootPane != null;
1831:
1832: if (actionMappingInfo == null) {
1833: findActionMappings();
1834: }
1835:
1836: assert actionMappingInfo != null;
1837: assert actionMappingInfo.length == mnemonicBlocked.length;
1838:
1839: final JComponent[] comps = mnemonicBlocked;
1840: for (int i = 0; i < comps.length; i++) {
1841: ActionMappingInfo mappingInfo = actionMappingInfo[i];
1842: if (mappingInfo != null) {
1843: comps[i].getActionMap().put(
1844: mappingInfo.actionKey,
1845: new SelectSrcGrpAction(comps[i],
1846: mappingInfo.originalAction));
1847: } else if (comps[i] instanceof JLabel) {
1848: ActionMap map = new JLabelActionMap(comps[i]);
1849: map.setParent(comps[i].getActionMap());
1850: comps[i].setActionMap(map);
1851: continue;
1852: }
1853: }
1854: }
1855:
1856: /**
1857: */
1858: private void unblockMnemonics() {
1859: assert rootPane != null;
1860:
1861: if (actionMappingInfo == null) {
1862:
1863: /* blockMnemonics() did not pass */
1864: return;
1865: }
1866:
1867: assert actionMappingInfo.length == mnemonicBlocked.length;
1868:
1869: final JComponent[] comps = mnemonicBlocked;
1870: for (int i = 0; i < comps.length; i++) {
1871: ActionMappingInfo mappingInfo = actionMappingInfo[i];
1872: if (mappingInfo != null) {
1873: comps[i]
1874: .getActionMap()
1875: .put(
1876: mappingInfo.actionKey,
1877: mappingInfo.inProximateActionMap ? mappingInfo.originalAction
1878: : (Action) null);
1879: } else if (comps[i] instanceof JLabel) {
1880: comps[i].setActionMap(comps[i].getActionMap()
1881: .getParent());
1882: }
1883: }
1884: }
1885:
1886: /**
1887: */
1888: private void findActionMappings() {
1889: assert mnemonicBlocked != null;
1890:
1891: final String actionKey1 = "pressed"; //NOI18N
1892: final String actionKey2 = "press"; //NOI18N
1893:
1894: actionMappingInfo = new ActionMappingInfo[mnemonicBlocked.length];
1895:
1896: final JComponent[] comps = mnemonicBlocked;
1897: for (int i = 0; i < comps.length; i++) {
1898: JComponent c = comps[i];
1899:
1900: ActionMap actionMap = comps[i].getActionMap();
1901:
1902: String primaryKey = actionKey1;
1903: String secondaryKey = actionKey2;
1904:
1905: if (c instanceof JLabel) {
1906: actionMappingInfo[i] = null;
1907: continue;
1908: }
1909:
1910: String actionKey;
1911: Action originalAction = actionMap
1912: .get(actionKey = primaryKey);
1913: if (originalAction == null) {
1914: originalAction = actionMap
1915: .get(actionKey = secondaryKey);
1916: }
1917: if (originalAction == null) {
1918: ErrorManager
1919: .getDefault()
1920: .log(
1921: ErrorManager.EXCEPTION,
1922: "JUnitWizard - Test for Existing Class: " //NOI18N
1923: + "press action not found for a " //NOI18N
1924: + c.getClass().getName()
1925: + " component"); //NOI18N
1926: actionMappingInfo[i] = null;
1927: continue;
1928: }
1929:
1930: ActionMappingInfo mappingInfo = new ActionMappingInfo();
1931: mappingInfo.actionKey = actionKey;
1932: mappingInfo.originalAction = originalAction;
1933: /*mappingInfo.inProximateActionMap = false;*///it's the default
1934: /* find whether the mapping is defined in the proximate ActionMap */
1935: final String keyToFind = actionKey;
1936: final Object[] keys = actionMap.keys();
1937: if (keys != null) {
1938: for (int j = 0; j < keys.length; j++) {
1939: if (keyToFind.equals(keys[j])) {
1940: mappingInfo.inProximateActionMap = true;
1941: break;
1942: }
1943: }
1944: }
1945:
1946: actionMappingInfo[i] = mappingInfo;
1947: }
1948: }
1949:
1950: /**
1951: * Contains information about <code>ActionMap</code> mapping
1952: * of a UI component.
1953: * There is one instance of this class per each JComponent
1954: * in the {@link #mnemonicBlocked} array.
1955: */
1956: private static class ActionMappingInfo {
1957: /** action key for action which activates the component */
1958: String actionKey;
1959: /** original action mapped to the actionKey */
1960: Action originalAction;
1961: /**
1962: * true if the mapping was defined in the component's
1963: * proximate ActionMap; false otherwise
1964: */
1965: boolean inProximateActionMap;
1966: }
1967:
1968: /**
1969: * <!-- PENDING -->
1970: */
1971: final class JLabelActionMap extends ActionMap {
1972:
1973: private final Component component;
1974:
1975: JLabelActionMap(Component comp) {
1976: super ();
1977: this .component = comp;
1978: }
1979:
1980: @Override
1981: public Action get(Object key) {
1982: if (key.equals("press")) { //NOI18N
1983: Action defaultAction = super .get(key);
1984: return (defaultAction != null) ? new SelectSrcGrpAction(
1985: component, defaultAction)
1986: : null;
1987: } else {
1988: return super .get(key);
1989: }
1990: }
1991:
1992: }
1993:
1994: /**
1995: * Sets up a glass pane - one part of the focus change detection mechanism.
1996: */
1997: private void blockMouseClicks() {
1998: assert rootPane != null;
1999:
2000: final Component glassPane = rootPane.getGlassPane();
2001:
2002: if (glassPaneListener == null) {
2003: glassPaneListener = new GlassPaneListener();
2004: }
2005: glassPane.addMouseListener(glassPaneListener);
2006: glassPane.addMouseMotionListener(glassPaneListener);
2007: glassPane.setVisible(true);
2008: }
2009:
2010: /**
2011: * Cleans up a glass pane - one part of the focus change detection
2012: * mechanism.
2013: */
2014: private void unblockMouseClicks() {
2015: assert rootPane != null;
2016:
2017: if (glassPaneListener == null) {
2018: return;
2019: }
2020:
2021: final Component glassPane = rootPane.getGlassPane();
2022:
2023: glassPane.setVisible(false);
2024: glassPane.removeMouseMotionListener(glassPaneListener);
2025: glassPane.removeMouseListener(glassPaneListener);
2026: }
2027:
2028: /**
2029: *
2030: */
2031: final class GlassPaneListener implements MouseInputListener {
2032: final Component glassPane = rootPane.getGlassPane();
2033: final Component layeredPane = rootPane.getLayeredPane();
2034: final Container contentPane = rootPane.getContentPane();
2035:
2036: public void mouseMoved(MouseEvent e) {
2037: redispatchEvent(e);
2038: }
2039:
2040: public void mouseDragged(MouseEvent e) {
2041: redispatchEvent(e);
2042: }
2043:
2044: public void mouseClicked(MouseEvent e) {
2045: redispatchEvent(e);
2046: }
2047:
2048: public void mouseEntered(MouseEvent e) {
2049: redispatchEvent(e);
2050: }
2051:
2052: public void mouseExited(MouseEvent e) {
2053: redispatchEvent(e);
2054: }
2055:
2056: public void mousePressed(MouseEvent e) {
2057: evaluateEvent(e);
2058: }
2059:
2060: public void mouseReleased(MouseEvent e) {
2061: redispatchEvent(e);
2062: }
2063:
2064: private void evaluateEvent(MouseEvent e) {
2065: assert multipleSourceRoots;
2066:
2067: Component component = getDeepestComponent(e);
2068: if (component == null) {
2069: return;
2070: }
2071:
2072: boolean isBlocked = false;
2073: if (SwingUtilities.isLeftMouseButton(e)) {
2074: final Component[] blocked = mouseBlocked;
2075: for (int i = 0; i < blocked.length; i++) {
2076: if (component == blocked[i]) {
2077: isBlocked = true;
2078: break;
2079: }
2080: }
2081: }
2082:
2083: boolean askUserToChoose;
2084: SourceGroup[] candidates = null;
2085: if (!isBlocked || interactionRestrictionsSuspended) {
2086: askUserToChoose = false;
2087: } else if (component == defaultButton) {
2088: candidates = findParentGroupCandidates();
2089: askUserToChoose = (candidates.length > 1);
2090: } else if (!SwingUtilities.isDescendingFrom(component,
2091: visualComp)) {
2092: askUserToChoose = false;
2093: } else {
2094: candidates = findParentGroupCandidates();
2095: askUserToChoose = (candidates.length > 1);
2096: }
2097:
2098: assert (askUserToChoose == false)
2099: || (candidates.length > 1);
2100:
2101: if (askUserToChoose) {
2102: SourceGroup srcGroup = chooseSrcGroup(candidates);
2103: if (srcGroup != null) {
2104: setSelectedSrcGroup(srcGroup);
2105: focusGainAllowedFor = component;
2106: component.requestFocus();
2107: }
2108: } else {
2109: if (candidates != null) {
2110: assert candidates.length == 1;
2111:
2112: setSelectedSrcGroup(candidates[0]);
2113: }
2114: focusGainAllowedFor = component;
2115: try {
2116: redispatchEvent(e, component);
2117: } finally {
2118: clearFocusGainAllowedVar();
2119: }
2120: }
2121: }
2122:
2123: private void redispatchEvent(MouseEvent e) {
2124: Component deepestComp = getDeepestComponent(e);
2125: if (deepestComp != null) {
2126: redispatchEvent(e, deepestComp);
2127: }
2128: }
2129:
2130: private void redispatchEvent(MouseEvent e, Component component) {
2131: Point componentPoint = SwingUtilities.convertPoint(
2132: glassPane, e.getPoint(), component);
2133: component.dispatchEvent(new MouseEvent(component,
2134: e.getID(), e.getWhen(), e.getModifiers(),
2135: componentPoint.x, componentPoint.y, e
2136: .getClickCount(), e.isPopupTrigger()));
2137: }
2138:
2139: private Component getDeepestComponent(MouseEvent e) {
2140: Point contentPanePoint = SwingUtilities.convertPoint(
2141: glassPane, e.getPoint(), contentPane);
2142: return SwingUtilities.getDeepestComponentAt(contentPane,
2143: contentPanePoint.x, contentPanePoint.y);
2144: }
2145: }
2146:
2147: /**
2148: * Action that is activated by a mnemonic keystroke.
2149: */
2150: private class SelectSrcGrpAction extends AbstractAction {
2151: private final Component component;
2152: private final Action delegate;
2153:
2154: public SelectSrcGrpAction(Component comp, Action delegate) {
2155: this .component = comp;
2156: this .delegate = delegate;
2157: }
2158:
2159: public void actionPerformed(ActionEvent e) {
2160: assert multipleSourceRoots;
2161:
2162: boolean askUserToChoose;
2163: SourceGroup[] candidates = null;
2164: if (interactionRestrictionsSuspended) {
2165: askUserToChoose = false;
2166: } else if ((component == defaultButton)
2167: || (component == rootPane)) {
2168: candidates = findParentGroupCandidates();
2169: askUserToChoose = (candidates.length > 1);
2170: } else if (!SwingUtilities.isDescendingFrom(component,
2171: visualComp)) {
2172: askUserToChoose = false;
2173: } else {
2174: candidates = findParentGroupCandidates();
2175: askUserToChoose = (candidates.length > 1);
2176: }
2177:
2178: assert (askUserToChoose == false)
2179: || (candidates.length > 1);
2180:
2181: if (askUserToChoose) {
2182: SourceGroup srcGroup = chooseSrcGroup(candidates);
2183: if (srcGroup != null) {
2184: setSelectedSrcGroup(srcGroup);
2185: if (component == rootPane) {
2186: defaultButton.requestFocus();
2187: } else {
2188: component.requestFocus();
2189: }
2190: }
2191: } else {
2192: if (candidates != null) {
2193: assert candidates.length == 1;
2194:
2195: setSelectedSrcGroup(candidates[0]);
2196: }
2197: redispatchEvent(e);
2198: }
2199: }
2200:
2201: private void redispatchEvent(ActionEvent e) {
2202: focusGainAllowedFor = component;
2203: try {
2204: delegate.actionPerformed(e);
2205: } finally {
2206: clearFocusGainAllowedVar();
2207: }
2208: }
2209:
2210: @Override
2211: public boolean isEnabled() {
2212: return delegate.isEnabled();
2213: }
2214: }
2215:
2216: /**
2217: * <!-- PENDING -->
2218: */
2219: private class ClsNameDocumentFilter extends DocumentFilter {
2220: public ClsNameDocumentFilter() {
2221: }
2222:
2223: @Override
2224: public void replace(DocumentFilter.FilterBypass bypass,
2225: int offset, int length, String text, AttributeSet attrs)
2226: throws BadLocationException {
2227: if (!programmaticChange && srcGroupNameDisplayed) {
2228: removeSrcGroupName(bypass);
2229: }
2230: super .replace(bypass, offset, length, text, attrs);
2231: }
2232:
2233: @Override
2234: public void insertString(DocumentFilter.FilterBypass bypass,
2235: int offset, String string, AttributeSet attr)
2236: throws BadLocationException {
2237: if (!programmaticChange && srcGroupNameDisplayed) {
2238: removeSrcGroupName(bypass);
2239: }
2240: super .insertString(bypass, offset, string, attr);
2241: }
2242:
2243: @Override
2244: public void remove(DocumentFilter.FilterBypass bypass,
2245: int offset, int length) throws BadLocationException {
2246: if (!programmaticChange && srcGroupNameDisplayed) {
2247: removeSrcGroupName(bypass);
2248: }
2249: super .remove(bypass, offset, length);
2250: }
2251:
2252: private void removeSrcGroupName(
2253: DocumentFilter.FilterBypass bypass)
2254: throws BadLocationException {
2255: bypass.remove(classNameLength, tfClassToTest.getText()
2256: .length()
2257: - classNameLength);
2258: srcGroupNameDisplayed = false;
2259: setNavigationFilterEnabled(false);
2260: }
2261: }
2262:
2263: /**
2264: * <!-- PENDING -->
2265: */
2266: private class ClsNameNavigationFilter extends NavigationFilter {
2267: public ClsNameNavigationFilter() {
2268: }
2269:
2270: @Override
2271: public void setDot(NavigationFilter.FilterBypass bypass,
2272: int dot, Position.Bias bias) {
2273: if (dot > classNameLength) {
2274: bypass.setDot(classNameLength, bias);
2275: } else {
2276: super .setDot(bypass, dot, bias);
2277: }
2278: }
2279:
2280: @Override
2281: public void moveDot(NavigationFilter.FilterBypass bypass,
2282: int dot, Position.Bias bias) {
2283: if (dot > classNameLength) {
2284: bypass.moveDot(classNameLength, bias);
2285: } else {
2286: super .moveDot(bypass, dot, bias);
2287: }
2288: }
2289:
2290: public void ensureCursorInRange() {
2291: if (srcGroupNameDisplayed) {
2292: if (tfClassToTest.getCaretPosition() > classNameLength) {
2293: tfClassToTest.setCaretPosition(classNameLength);
2294: }
2295: }
2296: }
2297: }
2298:
2299: /**
2300: * Sends a request to clear the {@link #focusGainAllowedFor} variable
2301: * to the end of the event queue.
2302: */
2303: private void clearFocusGainAllowedVar() {
2304: SwingUtilities.invokeLater(new Runnable() {
2305: public void run() {
2306: focusGainAllowedFor = null;
2307: }
2308: });
2309: }
2310:
2311: /**
2312: */
2313: private static boolean checkObjChanged(Object oldObj, Object newObj) {
2314: return ((oldObj != null) || (newObj != null))
2315: && ((oldObj == null) || !oldObj.equals(newObj));
2316: }
2317:
2318: /* *
2319: * /
2320: private static void printCallstack(String header) {
2321:
2322: //PENDING - this method is not needed in the final version
2323:
2324: if (header != null) {
2325: System.out.println(header);
2326: }
2327:
2328: StackTraceElement[] frames = new Exception().getStackTrace();
2329: int count = Math.min(5, frames.length);
2330: for (int i = 1; i < frames.length; i++) {
2331: String methodName = frames[i].getMethodName();
2332: if (!methodName.startsWith("access$")) {
2333: System.out.println(" " + methodName + "(...)");
2334: if (--count == 0) {
2335: break;
2336: }
2337: }
2338: }
2339: System.out.println();
2340: }
2341: */
2342:
2343: }
|