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;
042:
043: import java.awt.Component;
044: import java.awt.Container;
045: import java.awt.Point;
046: import java.awt.Rectangle;
047: import java.io.IOException;
048: import javax.swing.JComponent;
049: import org.netbeans.jemmy.QueueTool;
050: import org.netbeans.jemmy.TestOut;
051: import org.netbeans.swing.tabcontrol.*;
052: import org.netbeans.core.windows.view.ui.tabcontrol.TabbedAdapter;
053: import org.netbeans.jellytools.actions.AttachWindowAction;
054: import org.netbeans.jellytools.actions.CloneViewAction;
055: import org.netbeans.jellytools.actions.CloseAllDocumentsAction;
056: import org.netbeans.jellytools.actions.CloseViewAction;
057: import org.netbeans.jellytools.actions.MaximizeWindowAction;
058: import org.netbeans.jellytools.actions.RestoreWindowAction;
059: import org.netbeans.jemmy.ComponentChooser;
060: import org.netbeans.jemmy.JemmyException;
061: import org.netbeans.jemmy.JemmyProperties;
062: import org.netbeans.jemmy.Timeouts;
063: import org.netbeans.jemmy.Waitable;
064: import org.netbeans.jemmy.Waiter;
065: import org.netbeans.jemmy.drivers.DriverManager;
066: import org.netbeans.jemmy.drivers.input.MouseRobotDriver;
067: import org.netbeans.jemmy.operators.ContainerOperator;
068: import org.netbeans.jemmy.operators.JComponentOperator;
069: import org.netbeans.jemmy.operators.JPopupMenuOperator;
070: import org.netbeans.jemmy.operators.Operator;
071: import org.openide.cookies.SaveCookie;
072: import org.openide.loaders.DataObject;
073: import org.openide.windows.TopComponent;
074: import org.netbeans.core.multiview.MultiViewCloneableTopComponent;
075:
076: /** Represents org.openide.windows.TopComponent. It is IDE wrapper for a lot of
077: * panels in IDE. TopComponent is for example Filesystems panel, every editor
078: * panel or execution panel. TopComponent can be located by TopComponentOperator
079: * anywhere inside IDE, if it is opened. It is by default activated which means
080: * it is put to foreground if there exist more top components in a split area.
081: * TopComponent can also be located explicitly inside some Container.
082: *
083: * <p>
084: * Usage:<br>
085: * <pre>
086: * TopComponentOperator tco = new TopComponentOperator("Execution");
087: * tco.pushMenuOnTab("Maximize");
088: * tco.restore();
089: * tco.attachTo("Filesystems", AttachWindowAction.AS_LAST_TAB);
090: * tco.attachTo("Output", AttachWindowAction.RIGHT);
091: * tco.close();
092: * </pre>
093: * @author Adam.Sotona@sun.com
094: * @author Jiri.Skrivanek@sun.com
095: *
096: * @see org.netbeans.jellytools.actions.AttachWindowAction
097: * @see org.netbeans.jellytools.actions.CloneViewAction
098: * @see org.netbeans.jellytools.actions.CloseAllDocumentsAction
099: * @see org.netbeans.jellytools.actions.CloseViewAction
100: * @see org.netbeans.jellytools.actions.MaximizeWindowAction
101: * @see org.netbeans.jellytools.actions.RestoreWindowAction
102: */
103: public class TopComponentOperator extends JComponentOperator {
104: /** "Close Window" popup menu item. */
105: private static final String closeWindowItem = Bundle
106: .getStringTrimmed(
107: "org.netbeans.core.windows.actions.Bundle",
108: "LBL_CloseWindowAction");
109: /** "Close All Documents" popup menu item. */
110: private static final String closeAllDocumentsItem = Bundle
111: .getStringTrimmed(
112: "org.netbeans.core.windows.actions.Bundle",
113: "LBL_CloseAllDocumentsAction");
114:
115: static {
116: // Checks if you run on correct jemmy version. Writes message to jemmy log if not.
117: JellyVersion.checkJemmyVersion();
118: // need to set timeout for the case it was not set previously
119: JemmyProperties.getCurrentTimeouts().initDefault(
120: "EventDispatcher.RobotAutoDelay", 0);
121: DriverManager.setDriver(DriverManager.MOUSE_DRIVER_ID,
122: new MouseRobotDriver(JemmyProperties
123: .getCurrentTimeouts().create(
124: "EventDispatcher.RobotAutoDelay"),
125: new String[] { TopComponentOperator.class
126: .getName() }));
127: }
128:
129: /** Waits for index-th TopComponent with given name in specified container.
130: * It is activated by default.
131: * @param contOper container where to search
132: * @param topComponentName name of TopComponent (it used to be label of tab)
133: * @param index index of TopComponent to be find
134: */
135: public TopComponentOperator(ContainerOperator contOper,
136: String topComponentName, int index) {
137: super (waitTopComponent(contOper, topComponentName, index, null));
138: copyEnvironment(contOper);
139: makeComponentVisible();
140: }
141:
142: /** Waits for TopComponent with given name in specified container.
143: * It is activated by default.
144: * @param contOper container where to search
145: * @param topComponentName name of TopComponent (it used to be label of tab)
146: */
147: public TopComponentOperator(ContainerOperator contOper,
148: String topComponentName) {
149: this (contOper, topComponentName, 0);
150: }
151:
152: /** Waits for index-th TopComponent in specified container.
153: * It is activated by default.
154: * @param contOper container where to search
155: * @param index index of TopComponent to be find
156: */
157: public TopComponentOperator(ContainerOperator contOper, int index) {
158: this (contOper, null, index);
159: }
160:
161: /** Waits for first TopComponent in specified container.
162: * It is activated by default.
163: * @param contOper container where to search
164: */
165: public TopComponentOperator(ContainerOperator contOper) {
166: this (contOper, null, 0);
167: }
168:
169: /** Waits for index-th TopComponent with given name in whole IDE.
170: * It is activated by default.
171: * @param topComponentName name of TopComponent (it used to be label of tab)
172: * @param index index of TopComponent to be find
173: */
174: public TopComponentOperator(String topComponentName, int index) {
175: this (waitTopComponent(topComponentName, index));
176:
177: }
178:
179: /** Waits for first TopComponent with given name in whole IDE.
180: * It is activated by default.
181: * @param topComponentName name of TopComponent (it used to be label of tab)
182: */
183: public TopComponentOperator(String topComponentName) {
184: this (topComponentName, 0);
185: }
186:
187: /** Creates new instance from given TopComponent.
188: * It is activated by default.
189: * This constructor is used in properties.PropertySheetOperator.
190: * @param jComponent instance of JComponent
191: */
192: public TopComponentOperator(JComponent jComponent) {
193: super (jComponent);
194: makeComponentVisible();
195: }
196:
197: /** Makes active window in which this top component resides (main window
198: * in joined mode) and then activates this top component to be in the
199: * foreground.
200: */
201: public void makeComponentVisible() {
202: // Make active window in which this TopComponent resides.
203: // It is necessary e.g. for keyboard focus
204: super .makeComponentVisible();
205: // Check if it is really TopComponent. It doesn't have to be
206: // for example for PropertySheetOperator in Options window.
207: // In that case do nothing.
208: if (getSource() instanceof TopComponent) {
209: // activate TopComponent, i.e. switch tab control to be active.
210: // run in dispatch thread
211: runMapping(new MapVoidAction("requestActive") {
212: public void map() {
213: ((TopComponent) getSource()).requestActive();
214: }
215: });
216: }
217: }
218:
219: /** Attaches this top component to a new position defined by target top
220: * component and side.
221: * @param targetTopComponentName name of top component defining a position
222: * where to attach top component
223: * @param side side where to attach top component ({@link AttachWindowAction#LEFT},
224: * {@link AttachWindowAction#RIGHT}, {@link AttachWindowAction#TOP},
225: * {@link AttachWindowAction#BOTTOM}, {@link AttachWindowAction#AS_LAST_TAB})
226: */
227: public void attachTo(String targetTopComponentName, String side) {
228: new AttachWindowAction(targetTopComponentName, side)
229: .perform(this );
230: }
231:
232: /** Attaches this top component to a new position defined by target top
233: * component and side.
234: * @param targetTopComponentOperator operator of top component defining a position
235: * where to attach top component
236: * @param side side where to attach top component ({@link AttachWindowAction#LEFT},
237: * {@link AttachWindowAction#RIGHT}, {@link AttachWindowAction#TOP},
238: * {@link AttachWindowAction#BOTTOM}, {@link AttachWindowAction#AS_LAST_TAB})
239: */
240: public void attachTo(
241: TopComponentOperator targetTopComponentOperator, String side) {
242: new AttachWindowAction(targetTopComponentOperator, side)
243: .perform(this );
244: }
245:
246: /** Maximizes this top component. */
247: public void maximize() {
248: new MaximizeWindowAction().perform(this );
249: }
250:
251: /** Restores maximized window. */
252: public void restore() {
253: new RestoreWindowAction().perform(this );
254: }
255:
256: /** Clones this TopComponent. TopComponent is activated before
257: * action is performed. */
258: public void cloneDocument() {
259: new CloneViewAction().perform(this );
260: }
261:
262: /** Closes this TopComponent and wait until it is closed.
263: * TopComponent is activated before action is performed. */
264: public void closeWindow() {
265: if (isModified()) {
266: new Thread(new Runnable() {
267: public void run() {
268: pushMenuOnTab(closeWindowItem);
269: };
270: }, "thread to close TopComponent").start();
271: } else {
272: new CloseViewAction().perform(this );
273: waitComponentShowing(false);
274: }
275: }
276:
277: /** Closes this TopComponent instance by IDE API call and wait until
278: * it is not closed. If this TopComponent is modified (e.g. editor top
279: * component), it discards possible changes.
280: * @see #close
281: */
282: public void closeDiscard() {
283: setUnmodified();
284: close();
285: }
286:
287: /** Finds DataObject for the content of this TopComponent and set it
288: * unmodified. Used in closeDiscard method.
289: */
290: public void setUnmodified() {
291: // should be just one node
292: org.openide.nodes.Node[] nodes = ((TopComponent) getSource())
293: .getActivatedNodes();
294: if (nodes == null) {
295: // try to find possible enclosing MultiviewTopComponent
296: TopComponentOperator parentTco = findParentTopComponent();
297: if (parentTco != null) {
298: parentTco.setUnmodified();
299: }
300: }
301: // TopComponent like Execution doesn't have any nodes associated
302: if (nodes != null) {
303: for (int i = 0; i < nodes.length; i++) {
304: DataObject dob = (DataObject) nodes[i]
305: .getCookie(DataObject.class);
306: dob.setModified(false);
307: }
308: }
309: }
310:
311: /** Returns true if this top component is modified (e.g. source in editor)
312: * @return boolean true if this object is modified; false otherwise
313: */
314: public boolean isModified() {
315: // should be just one node
316: org.openide.nodes.Node[] nodes = ((TopComponent) getSource())
317: .getActivatedNodes();
318: if (nodes == null) {
319: // try to find possible enclosing MultiviewTopComponent
320: TopComponentOperator parentTco = findParentTopComponent();
321: if (parentTco != null) {
322: return parentTco.isModified();
323: }
324: }
325: // TopComponent like Execution doesn't have any nodes associated
326: boolean modified = false;
327: if (nodes != null) {
328: for (int i = 0; i < nodes.length; i++) {
329: DataObject dob = (DataObject) nodes[i]
330: .getCookie(DataObject.class);
331: if (dob != null) {
332: // any from data objects is modified
333: modified = modified || dob.isModified();
334: }
335: }
336: }
337: return modified;
338: }
339:
340: /** Saves content of this TopComponent. If it is not applicable or content
341: * of TopComponent is not modified, it does nothing.
342: */
343: public void save() {
344: // should be just one node
345: org.openide.nodes.Node[] nodes = ((TopComponent) getSource())
346: .getActivatedNodes();
347: if (nodes == null) {
348: // try to find possible enclosing MultiviewTopComponent
349: TopComponentOperator parentTco = findParentTopComponent();
350: if (parentTco != null) {
351: parentTco.save();
352: }
353: }
354: // TopComponent like Execution doesn't have any nodes associated
355: if (nodes != null) {
356: for (int i = 0; i < nodes.length; i++) {
357: SaveCookie sc = (SaveCookie) nodes[i]
358: .getCookie(SaveCookie.class);
359: if (sc != null) {
360: try {
361: sc.save();
362: } catch (IOException e) {
363: throw new JemmyException(
364: "Exception while saving this TopComponent.",
365: e);
366: }
367: }
368: }
369: }
370: }
371:
372: /** Closes this TopComponent instance by IDE API call and wait until
373: * it is not closed. If this TopComponent is modified (e.g. editor top
374: * component), question dialog is shown and you have to close it. To close
375: * this TopComponent and discard possible changes use {@link #closeDiscard}
376: * method.
377: */
378: public void close() {
379: if (isModified()) {
380: // need to call it by popup because it is impossible to call
381: // TopComponent.close in AWT thread and handle question dialog in a different thread
382: closeWindow();
383: } else {
384: if (isOpened()) {
385: // run in dispatch thread
386: runMapping(new MapVoidAction("close") {
387: public void map() {
388: ((TopComponent) getSource()).close();
389: }
390: });
391: } else {
392: // try to find enclosing MultiviewTopComponent
393: TopComponentOperator parent = findParentTopComponent();
394: if (parent != null) {
395: parent.close();
396: }
397: }
398: waitComponentShowing(false);
399: }
400: }
401:
402: /** Closes all opened documents and waits until this top component is closed. */
403: public void closeAllDocuments() {
404: DataObject[] modifs = DataObject.getRegistry().getModified();
405: if (modifs.length != 0) {
406: // some object modified => need to call in new thread because modal question dialog appears
407: new Thread(new Runnable() {
408: public void run() {
409: pushMenuOnTab(closeAllDocumentsItem);
410: };
411: }, "thread to closeAllDocuments").start();
412: } else {
413: // no object modified
414: new CloseAllDocumentsAction().perform(this );
415: waitComponentShowing(false);
416: }
417: }
418:
419: /** Saves this document by popup menu on tab. */
420: public void saveDocument() {
421: // Save Document
422: String saveItem = Bundle.getStringTrimmed(
423: "org.netbeans.core.windows.actions.Bundle",
424: "LBL_SaveDocumentAction");
425: pushMenuOnTab(saveItem);
426: }
427:
428: /** Finds index-th TopComponent with given name in whole IDE.
429: * @param name name of TopComponent
430: * @param index index of TopComponent
431: * @return TopComponent instance or null if noone matching criteria was found
432: */
433: public static JComponent findTopComponent(String name, int index) {
434: return findTopComponent(null, name, index, null);
435: }
436:
437: /** Finds index-th TopComponent with given name in IDE registry.
438: * @param cont container where to search
439: * @param name name of TopComponent
440: * @param index index of TopComponent
441: * @param subchooser ComponentChooser to determine exact TopComponent
442: * @return TopComponent instance or null if noone matching criteria was found
443: */
444: protected static JComponent findTopComponent(
445: final ContainerOperator cont, final String name,
446: final int index, final ComponentChooser subchooser) {
447: // run in dispatch thread
448: return (JComponent) new QueueTool()
449: .invokeSmoothly(new QueueTool.QueueAction(
450: "findTopComponent") { // NOI18N
451: public Object launch() {
452: int counter = index;
453: Object tc[] = TopComponent.getRegistry()
454: .getOpened().toArray();
455: StringComparator comparator = cont == null ? Operator
456: .getDefaultStringComparator()
457: : cont.getComparator();
458: TopComponent c;
459: // loop through showing TopComponents
460: for (int i = 0; i < tc.length; i++) {
461: c = (TopComponent) tc[i];
462: if (c.isShowing()
463: && (comparator.equals(c.getName(),
464: name) || comparator.equals(
465: c.getDisplayName(), name))
466: && isUnder(cont, c)) {
467:
468: JComponent result = checkSubchooser(c,
469: subchooser);
470: if (result != null) {
471: if (--counter < 0) {
472: return result;
473: }
474: }
475: }
476: }
477: // loop through NOT showing TopComponents but parent has to be showing
478: for (int i = 0; i < tc.length; i++) {
479: c = (TopComponent) tc[i];
480: if ((!c.isShowing())
481: && isParentShowing(c)
482: && (comparator.equals(c.getName(),
483: name) || comparator.equals(
484: c.getDisplayName(), name))
485: && isUnder(cont, c)) {
486:
487: JComponent result = checkSubchooser(c,
488: subchooser);
489: if (result != null) {
490: if (--counter < 0) {
491: return result;
492: }
493: }
494: }
495: }
496: return null;
497: }
498: });
499: }
500:
501: /** If subchooser is null, return TopComponent.
502: * Else if c is instance of MultiViewCloneableTopComponent try to find
503: * and return sub component in MVCTC corresponding to sub chooser. Else
504: * check TC in sub chooser and return it if matches. MVCTC can host
505: * several views, e.g. source and design view in form editor or xml, servlets,
506: * overview views in web.xml editor. Then EditorOperator is able to find
507: * appropriate CloneableEditor in MVCTC.
508: * @param c TopComponent to check
509: * @param subchooser ComponentChooser to check if matches
510: * @return given TopComponent or appropriate sub component
511: */
512: private static JComponent checkSubchooser(TopComponent c,
513: ComponentChooser subchooser) {
514: if (subchooser == null) {
515: return c;
516: } else {
517: if (c instanceof MultiViewCloneableTopComponent) {
518: TopComponentOperator tco = new TopComponentOperator(
519: (JComponent) c);
520: // suppress output when finding sub component
521: tco.setOutput(TestOut.getNullOutput());
522: return (JComponent) tco.findSubComponent(subchooser);
523: } else {
524: if (subchooser.checkComponent(c)) {
525: return c;
526: }
527: }
528: }
529: return null;
530: }
531:
532: private static boolean isParentShowing(Component c) {
533: while (c != null) {
534: if (c.isShowing())
535: return true;
536: c = c.getParent();
537: }
538: return false;
539: }
540:
541: private static boolean isUnder(ContainerOperator cont, Component c) {
542: if (cont == null)
543: return true;
544: Component comp = cont.getSource();
545: while (comp != c && c != null)
546: c = c.getParent();
547: return (comp == c);
548: }
549:
550: /** Waits for index-th TopComponent with given name in IDE registry.
551: * It throws JemmyException when TopComponent is not find until timeout
552: * expires.
553: * @param name name of TopComponent
554: * @param index index of TopComponent
555: * @return TopComponent instance or throws JemmyException if not found
556: * @see #findTopComponent
557: */
558: protected static JComponent waitTopComponent(final String name,
559: final int index) {
560: return waitTopComponent(null, name, index, null);
561: }
562:
563: /** Waits for index-th TopComponent with given name in IDE registry.
564: * It throws JemmyException when TopComponent is not find until timeout
565: * expires.
566: * @param cont container where to search
567: * @param name name of TopComponent
568: * @param index index of TopComponent
569: * @param subchooser ComponentChooser to determine exact TopComponent
570: * @return TopComponent instance or throws JemmyException if not found
571: * @see #findTopComponent
572: */
573: protected static JComponent waitTopComponent(
574: final ContainerOperator cont, final String name,
575: final int index, final ComponentChooser subchooser) {
576: try {
577: Waiter waiter = new Waiter(new Waitable() {
578: public Object actionProduced(Object obj) {
579: return findTopComponent(cont, name, index,
580: subchooser);
581: }
582:
583: public String getDescription() {
584: return ("Wait TopComponent with name="
585: + name
586: + " index="
587: + String.valueOf(index)
588: + (subchooser == null ? "" : " subchooser="
589: + subchooser.getDescription()) + " loaded");
590: }
591: });
592: Timeouts times = JemmyProperties.getCurrentTimeouts()
593: .cloneThis();
594: times
595: .setTimeout(
596: "Waiter.WaitingTime",
597: times
598: .getTimeout("ComponentOperator.WaitComponentTimeout"));
599: waiter.setTimeouts(times);
600: waiter.setOutput(JemmyProperties.getCurrentOutput());
601: return ((JComponent) waiter.waitAction(null));
602: } catch (InterruptedException e) {
603: return (null);
604: }
605: }
606:
607: /** Makes top component active and pushes given menu on its tab.
608: * @param popupPath menu path separated by '|' (e.g. "CVS|Refresh")
609: */
610: public void pushMenuOnTab(String popupPath) {
611: if (isOpened()) {
612: this .makeComponentVisible();
613: TabbedAdapter ta = findTabbedAdapter();
614: int index = ta.indexOf((TopComponent) getSource());
615:
616: Rectangle r = new Rectangle();
617: ta.getTabRect(index, r);
618: Point p = new Point(r.x + (r.width / 2), r.y
619: + (r.height / 2));
620: Component tabsComp = ta.getComponentAt(p);
621: new JPopupMenuOperator(JPopupMenuOperator.callPopup(
622: tabsComp, p.x, p.y)).pushMenu(popupPath);
623: } else {
624: // try to find enclosing MultiviewTopComponent
625: TopComponentOperator parent = findParentTopComponent();
626: if (parent != null) {
627: parent.pushMenuOnTab(popupPath);
628: }
629: }
630: }
631:
632: /** Returns TabbedAdapter component from parents hierarchy.
633: * Used also in EditorWindowOperator.
634: */
635: TabbedAdapter findTabbedAdapter() {
636: Component parent = getSource().getParent();
637: while (parent != null) {
638: if (parent instanceof TabbedAdapter) {
639: return (TabbedAdapter) parent;
640: } else {
641: parent = parent.getParent();
642: }
643: }
644: return null;
645: }
646:
647: Container findTabDisplayer() {
648: return ContainerOperator.findContainer(findTabbedAdapter(),
649: new ComponentChooser() {
650: public boolean checkComponent(Component comp) {
651: return comp.getClass().getName().endsWith(
652: "TabDisplayer");
653: }
654:
655: public String getDescription() {
656: return "org.netbeans.swing.tabcontrol.TabDisplayer";
657: }
658: });
659: }
660:
661: /**
662: * Waits the topcomponent to be closed.
663: */
664: public void waitClosed() {
665: getOutput().printLine(
666: "Wait topcomponent to be closed \n : "
667: + getSource().toString());
668: getOutput().printGolden("Wait topcomponent to be closed");
669: waitState(new ComponentChooser() {
670: public boolean checkComponent(Component comp) {
671: return (!comp.isVisible());
672: }
673:
674: public String getDescription() {
675: return ("Closed topcomponent");
676: }
677: });
678: }
679:
680: /** Returns true if this TopComponent is opened. If it is not opened, it
681: * usually means it is contained within MultiviewTopComponent.
682: * @return true if open, false otherwise
683: */
684: protected boolean isOpened() {
685: // run in dispatch thread
686: return runMapping(new MapBooleanAction("isOpened") { // NOI18N
687: public boolean map() {
688: return ((TopComponent) getSource()).isOpened();
689: }
690: });
691: }
692:
693: /** Returns TopComponentOperator from parents hierarchy. It should be
694: * MultiviewTopComponent.
695: */
696: protected TopComponentOperator findParentTopComponent() {
697: Component parent = getSource().getParent();
698: while (parent != null) {
699: if (parent instanceof TopComponent) {
700: return new TopComponentOperator((JComponent) parent);
701: } else {
702: parent = parent.getParent();
703: }
704: }
705: return null;
706: }
707: }
|