001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.jellytools.properties;
042:
043: import java.awt.Component;
044: import java.awt.Frame;
045: import java.awt.Window;
046: import javax.swing.JComponent;
047: import org.netbeans.core.NbSheet;
048: import org.netbeans.jellytools.Bundle;
049: import org.netbeans.jellytools.MainWindowOperator;
050: import org.netbeans.jellytools.TopComponentOperator;
051: import org.netbeans.jellytools.actions.HelpAction;
052: import org.netbeans.jellytools.actions.PropertiesAction;
053: import org.netbeans.jellytools.actions.ShowDescriptionAreaAction;
054: import org.netbeans.jellytools.actions.SortByCategoryAction;
055: import org.netbeans.jellytools.actions.SortByNameAction;
056: import org.netbeans.jemmy.ComponentChooser;
057: import org.netbeans.jemmy.JemmyException;
058: import org.netbeans.jemmy.JemmyProperties;
059: import org.netbeans.jemmy.TestOut;
060: import org.netbeans.jemmy.Timeouts;
061: import org.netbeans.jemmy.Waitable;
062: import org.netbeans.jemmy.Waiter;
063: import org.netbeans.jemmy.operators.ContainerOperator;
064: import org.netbeans.jemmy.operators.JButtonOperator;
065: import org.netbeans.jemmy.operators.JEditorPaneOperator;
066: import org.netbeans.jemmy.operators.JLabelOperator;
067: import org.netbeans.jemmy.operators.JTableOperator;
068: import org.netbeans.jemmy.operators.WindowOperator;
069: import org.openide.explorer.propertysheet.PropertySheet;
070: import org.openide.windows.TopComponent;
071:
072: /**
073: * Handles org.openide.explorer.propertysheet.PropertySheet which
074: * represents IDE property sheet TopComponent.
075: * It includes JTable with properties and optional description area.
076: * Use {@link Property} class or its descendants to work with properties.
077: * <p>
078: * Usage:<br>
079: * <pre>
080: PropertySheetOperator pso = new PropertySheetOperator("Properties of MyClass");
081: new Property(pso, "Arguments").setValue("arg1 arg2");
082: pso.sortByName();
083: System.out.println("Number of properties="+pso.tblSheet().getRowCount());
084: pso.sortByCategory();
085: * </pre>
086: *
087: * @author Jiri.Skrivanek@sun.com
088: * @see Property
089: * @see PropertiesAction
090: * @see SortByCategoryAction
091: * @see SortByNameAction
092: * @see ShowDescriptionAreaAction
093: * @see HelpAction
094: */
095: public class PropertySheetOperator extends TopComponentOperator {
096: // In IDE PropertySheet extends JPanel (parent org.netbeans.core.NbSheet extends TopComponent).
097: /* In new window system global property sheet resides in main window and extends TopComponent.
098: * Other property sheets are opened in dialog and they do not behave as TopComponents.
099: */
100:
101: /** JTable representing property sheet. */
102: private JTableOperator _tblSheet;
103: private JLabelOperator _lblDescriptionHeader;
104: private JEditorPaneOperator _txtDescription;
105: private JButtonOperator _btHelp;
106:
107: /** "No Properties" property sheet. */
108: public static final int MODE_NO_PROPERTIES = 0;
109: /** "Properties of" property sheet. */
110: public static final int MODE_PROPERTIES_OF_ONE_OBJECT = 1;
111: /** "Properties of Multiple Objects" property sheet. */
112: public static final int MODE_PROPERTIES_OF_MULTIPLE_OBJECTS = 2;
113:
114: /** Generic constructor
115: * @param sheet instance of PropertySheet
116: */
117: public PropertySheetOperator(JComponent sheet) {
118: super (sheet);
119: }
120:
121: /** Waits for property sheet anywhere in IDE. */
122: public PropertySheetOperator() {
123: this (waitPropertySheet(null, 0));
124: }
125:
126: /** Waits for property sheet with name according to given mode ("No Properties",
127: * "Properties of" or "Properties of Multiple Objects").
128: * @param mode type of shown properties
129: * @see #MODE_NO_PROPERTIES
130: * @see #MODE_PROPERTIES_OF_ONE_OBJECT
131: * @see #MODE_PROPERTIES_OF_MULTIPLE_OBJECTS
132: */
133: public PropertySheetOperator(int mode) {
134: this (mode, "");
135: }
136:
137: /** Waits for property sheet with name according to given mode ("No Properties",
138: * "Properties of" or "Properties of Multiple Objects") plus objectName
139: * in case of one object property sheet. In case of usage
140: * <code>
141: * new PropertySheetOperator(PropertySheetOperator.MODE_PROPERTIES_OF_ONE_OBJECT, "MyClass");
142: * </code>
143: * will be searched property sheet with name "Properties of MyClass" (on
144: * English locale).
145: * @param mode type of shown properties
146: * @param objectName name of object for that properties are shown (e.g. "MyClass")
147: * @see #MODE_NO_PROPERTIES
148: * @see #MODE_PROPERTIES_OF_ONE_OBJECT
149: * @see #MODE_PROPERTIES_OF_MULTIPLE_OBJECTS
150: */
151: public PropertySheetOperator(int mode, String objectName) {
152: this (Bundle.getString("org.netbeans.core.Bundle",
153: "CTL_FMT_GlobalProperties", new Object[] {
154: new Integer(mode), objectName }));
155: }
156:
157: /** Waits for property sheet with given name. Typically sheet
158: * name is used as window title.
159: * @param sheetName name of sheet to find (e.g. "Properties of MyClass")
160: */
161: public PropertySheetOperator(String sheetName) {
162: this (waitPropertySheet(sheetName, 0));
163: }
164:
165: /** Waits for property sheet with given name in specified
166: * container.
167: * @param contOper where to find
168: * @param sheetName name of sheet to find (e.g. "Properties of MyClass")
169: */
170: public PropertySheetOperator(ContainerOperator contOper,
171: String sheetName) {
172: super ((JComponent) contOper
173: .waitSubComponent(new PropertySheetSubchooser(
174: sheetName, contOper.getComparator())));
175: copyEnvironment(contOper);
176: }
177:
178: /** Waits for property sheet in specified ContainerOperator.
179: * It is for example PropertySheet in Options window.
180: * @param contOper where to find
181: */
182: public PropertySheetOperator(ContainerOperator contOper) {
183: this (contOper, 0);
184: }
185:
186: /** Waits for index-th property sheet in specified ContainerOperator.
187: * @param contOper where to find
188: * @param index int index
189: */
190: public PropertySheetOperator(ContainerOperator contOper, int index) {
191: super ((JComponent) contOper.waitSubComponent(
192: new PropertySheetSubchooser(), index));
193: copyEnvironment(contOper);
194: }
195:
196: /** Invokes properties by default action on currently selected object.
197: * @return instance of PropertySheetOperator
198: * @see org.netbeans.jellytools.actions.PropertiesAction
199: */
200: public static PropertySheetOperator invoke() {
201: new PropertiesAction().perform();
202: return new PropertySheetOperator();
203: }
204:
205: /** Returns JTableOperator representing SheetTable of this property sheet.
206: * @return instance of JTableOperator
207: */
208: public JTableOperator tblSheet() {
209: if (_tblSheet == null) {
210: _tblSheet = new JTableOperator(this );
211: }
212: return _tblSheet;
213: }
214:
215: /** Returns JLabelOperator representing header of description area.
216: * @return instance of JLabelOperator
217: */
218: public JLabelOperator lblDescriptionHeader() {
219: if (_lblDescriptionHeader == null) {
220: _lblDescriptionHeader = new JLabelOperator(this );
221: }
222: return _lblDescriptionHeader;
223: }
224:
225: /** Returns JTextAreaOperator representing text from description area.
226: * @return instance of JTextAreaOperator
227: */
228: public JEditorPaneOperator txtDescription() {
229: if (_txtDescription == null) {
230: _txtDescription = new JEditorPaneOperator(this );
231: }
232: return _txtDescription;
233: }
234:
235: /** Returns JButtonOperator representing help button of description area.
236: * @return instance of JButtonOperator
237: */
238: public JButtonOperator btHelp() {
239: if (_btHelp == null) {
240: _btHelp = new JButtonOperator(this );
241: }
242: return _btHelp;
243: }
244:
245: /** Gets text of header from description area.
246: * @return text of header from description area
247: */
248: public String getDescriptionHeader() {
249: return lblDescriptionHeader().getText();
250: }
251:
252: /** Gest description from description area.
253: * @return description from description area.
254: */
255: public String getDescription() {
256: return txtDescription().getText();
257: }
258:
259: /** Sorts properties by name by calling of popup menu on property sheet. */
260: public void sortByName() {
261: new SortByNameAction().perform(this );
262: }
263:
264: /** Sorts properties by category by calling of popup menu on property sheet. */
265: public void sortByCategory() {
266: new SortByCategoryAction().perform(this );
267: }
268:
269: /** Shows or hides description area depending on whether it is already shown
270: * or not. It just invokes Show description area popup menu item.
271: */
272: public void showDescriptionArea() {
273: new ShowDescriptionAreaAction().perform(this );
274: }
275:
276: /** Shows help by calling popup menu on property sheet. */
277: public void help() {
278: new HelpAction().performPopup(this );
279: }
280:
281: /** Performs verification by accessing all sub-components */
282: public void verify() {
283: tblSheet();
284: }
285:
286: /** Closes this property sheet and waits until
287: * it is not closed. In fact it closes container in which this property
288: * sheet is placed. It can be a TopComponent in the main window or in a separate
289: * frame, or a dialog.
290: */
291: @Override
292: public void close() {
293: if (getSource() instanceof TopComponent) {
294: // run in dispatch thread
295: boolean canClose = runMapping(new MapBooleanAction(
296: "canClose") {
297: public boolean map() {
298: return ((TopComponent) getSource()).canClose();
299: }
300: });
301: if (canClose) {
302: // if it is regular TopComponent and it can be closed
303: super .close();
304: return;
305: }
306: }
307: // close window where property sheet is hosted but not main window
308: if (getWindow() != MainWindowOperator.getDefault().getSource()) {
309: new WindowOperator(getWindow()).close();
310: }
311: }
312:
313: /** Finds property sheet anywhere in IDE. First it tries to find TopComponent
314: * representing global properties and if not found, it tries to find
315: * property sheet in all dialogs owned by Main Window or other frames.
316: * @param sheetName name of property sheet
317: * @param index index of property sheet
318: */
319: private static JComponent findPropertySheet(String sheetName,
320: int index) {
321: // try to find PS in MainWindow
322: JComponent comp = findTopComponent(null, sheetName, index,
323: new PropertySheetSubchooser());
324: if (comp != null) {
325: return comp;
326: }
327: // Try to find PS in a dialog which is owned by Main window or by other
328: // frame.
329: Frame[] frames = Frame.getFrames();
330: for (int frameIndex = 0; frameIndex < frames.length; frameIndex++) {
331: Window[] windows = frames[frameIndex].getOwnedWindows();
332: for (int i = 0; i < windows.length; i++) {
333: if (windows[i].isShowing()) {
334: // only showing windows are interesting
335: // create windows operator for found window
336: WindowOperator wo = new WindowOperator(windows[i]);
337: // supress output
338: wo.setOutput(TestOut.getNullOutput());
339: // try to find PropertySheet subcomponent
340: comp = (JComponent) wo.findSubComponent(
341: new PropertySheetSubchooser(sheetName,
342: MainWindowOperator.getDefault()
343: .getComparator()), index);
344: if (comp != null) {
345: return comp;
346: }
347: }
348: }
349: }
350: return null;
351: }
352:
353: /** Waits for property sheet anywhere in IDE. First it tries to find TopComponent
354: * representing global properties and if not found, it tries to find
355: * property sheet in all dialogs owned by Main Window or other frames.
356: * @param sheetName name of property sheet
357: * @param index index of property sheet
358: */
359: private static JComponent waitPropertySheet(final String sheetName,
360: final int index) {
361: try {
362: Waiter waiter = new Waiter(new Waitable() {
363: public Object actionProduced(Object obj) {
364: return findPropertySheet(sheetName, index);
365: }
366:
367: public String getDescription() {
368: return ("Wait PropertySheet with name=" + sheetName
369: + " index=" + index + " loaded");
370: }
371: });
372: Timeouts times = JemmyProperties.getCurrentTimeouts()
373: .cloneThis();
374: times
375: .setTimeout(
376: "Waiter.WaitingTime",
377: times
378: .getTimeout("ComponentOperator.WaitComponentTimeout"));
379: waiter.setTimeouts(times);
380: waiter.setOutput(JemmyProperties.getCurrentOutput());
381: return (JComponent) waiter.waitAction(null);
382: } catch (InterruptedException e) {
383: throw new JemmyException("Interrupted.", e);
384: }
385: }
386:
387: /** SubChooser to determine find property sheet.
388: * Used in constructors.
389: */
390: private static final class PropertySheetSubchooser implements
391: ComponentChooser {
392:
393: private String sheetName;
394: private StringComparator comparator;
395:
396: public PropertySheetSubchooser() {
397: }
398:
399: public PropertySheetSubchooser(String sheetName,
400: StringComparator comparator) {
401: this .sheetName = sheetName;
402: this .comparator = comparator;
403: }
404:
405: public boolean checkComponent(Component comp) {
406: if (comp instanceof PropertySheet
407: || comp instanceof NbSheet) {
408: if (sheetName == null) {
409: return true;
410: } else {
411: if (comp instanceof TopComponent) {
412: String name = ((TopComponent) comp)
413: .getDisplayName();
414: if (name == null) {
415: name = comp.getName();
416: }
417: return comparator.equals(name, sheetName);
418: }
419: }
420: }
421: return false;
422: }
423:
424: public String getDescription() {
425: return "org.openide.explorer.propertysheet.PropertySheet";
426: }
427: }
428: }
|