001: package abbot.editor;
002:
003: import java.awt.*;
004: import javax.swing.*;
005: import java.util.*;
006:
007: import abbot.util.AWT;
008: import abbot.finder.Hierarchy;
009:
010: /** Provides a condensed, more easily readable version of the original
011: hierarchy.
012: */
013: // TODO: figure out how to more cleanly put popups under their invokers (maybe
014: // have to scan the whole hierarchy each time).
015: public class CompactHierarchy implements Hierarchy {
016:
017: private boolean compact = true;
018: private Hierarchy hierarchy;
019:
020: public CompactHierarchy(Hierarchy original) {
021: this .hierarchy = original;
022: }
023:
024: public void setCompact(boolean compact) {
025: this .compact = compact;
026: }
027:
028: public boolean isCompact() {
029: return compact;
030: }
031:
032: public Collection getRoots() {
033: return hierarchy.getRoots();
034: }
035:
036: public Container getParent(Component c) {
037: // In the component hierarchy, show popup menus directly beneath their
038: // invoker
039: if (compact && c instanceof JPopupMenu) {
040: Component invoker = ((JPopupMenu) c).getInvoker();
041: if (invoker instanceof Container)
042: return (Container) invoker;
043: }
044: if (compact && c instanceof JToolTip) {
045: return ((JToolTip) c).getComponent();
046: }
047: Container parent = hierarchy.getParent(c);
048: if (compact) {
049: while (parent != null && isElided(parent)) {
050: parent = getParent(parent);
051: }
052: }
053: return parent;
054: }
055:
056: public boolean contains(Component c) {
057: return hierarchy.contains(c);
058: }
059:
060: public void dispose(Window w) {
061: hierarchy.dispose(w);
062: }
063:
064: /** Returns whether the given component is completely ignored (including
065: its children) in the hierarchy.
066: */
067: private boolean isIgnored(Component c) {
068: if (AWT.isTransientPopup(c))
069: return true;
070: return c instanceof JScrollBar
071: && c.getParent() instanceof JScrollPane;
072: }
073:
074: /** Returns whether the given component is omitted from the component
075: * hierarchy when compact is turned on (its children may be shown).
076: * For example, a JScrollPane's viewport is elided so that the scrolled
077: * content shows up directly beneath the scroll pane.
078: */
079: private boolean isElided(Component c) {
080: // JPanel or JWindow transient popups
081: if (AWT.isTransientPopup(c)) {
082: return true;
083: }
084: if (c instanceof Container) {
085: // Only windows that are heavyweight popups are elided
086: if (c instanceof Window)
087: return false;
088: if (c instanceof JPopupMenu
089: && ((JPopupMenu) c).getInvoker() instanceof JMenu)
090: return true;
091: // Content pane on heavyweight popup
092: if (AWT.isContentPane(c)
093: && AWT.isTransientPopup(SwingUtilities
094: .getWindowAncestor(c)))
095: return true;
096: }
097:
098: Container parent = c.getParent();
099: if (parent instanceof JScrollPane) {
100: // Ignore scrollbars and viewport, but not headers
101: return c instanceof JScrollBar || c instanceof JViewport;
102: }
103:
104: return parent instanceof RootPaneContainer
105: || parent instanceof JRootPane;
106: }
107:
108: /** Provide a list of a Component's children, sans any transient popups
109: * Keep track of any popups encountered.
110: */
111: // heavyweights are subwindows of Window?
112: // lightweights are subpanels of Window?
113: public Collection getComponents(Component c) {
114: if (c == null || !(c instanceof Container))
115: return new ArrayList();
116:
117: ArrayList list = new ArrayList();
118: if (compact) {
119: // Display menu contents directly beneath menus
120: if (c instanceof JMenu) {
121: return getComponents(((JMenu) c).getPopupMenu());
122: }
123: }
124: Iterator iter = hierarchy.getComponents(c).iterator();
125: while (iter.hasNext()) {
126: Component k = (Component) iter.next();
127: if (compact && isElided(k)) {
128: // Only add children if the component itself is not totally
129: // ignored.
130: if (!isIgnored(k)) {
131: list.addAll(getComponents(k));
132: }
133: } else {
134: list.add(k);
135: }
136: }
137:
138: list.addAll(findInvokerPopups(c));
139:
140: return list;
141: }
142:
143: /** Scan for popups which have been invoked by the given invoker. */
144: private Collection findInvokerPopups(Component invoker) {
145: ArrayList popups = new ArrayList();
146: Window root = AWT.getWindow(invoker);
147: // heavyweight popups are sub-windows of the invoker's window
148: Collection parents = new ArrayList(Arrays.asList(root
149: .getOwnedWindows()));
150: // lightweight popups show up on the window's layered pane
151: if (root instanceof JWindow || root instanceof JFrame
152: || root instanceof JDialog) {
153: JRootPane rp = ((RootPaneContainer) root).getRootPane();
154: // work around VM bug on RootPaneContainer.getLayeredPane()
155: // which sometimes results in a NPE
156: if (rp != null) {
157: JLayeredPane lp = rp.getLayeredPane();
158: if (lp != null) {
159: parents.addAll(Arrays.asList(lp.getComponents()));
160: }
161: }
162: }
163: Iterator iter = parents.iterator();
164: while (iter.hasNext()) {
165: Component c = (Component) iter.next();
166: JComponent popup = findInvokedPopup(invoker, c);
167: if (popup != null) {
168: popups.add(popup);
169: }
170: }
171: return popups;
172: }
173:
174: /** Returns the invoked popup found under the given parent (if any). */
175: private JComponent findInvokedPopup(Component invoker,
176: Component parent) {
177: if (AWT.isTransientPopup(parent)) {
178: JComponent popup = findPopup((Container) parent);
179: if (popup != null
180: && (!(popup instanceof JPopupMenu) || !(getParent(popup) instanceof JMenu))) {
181: if (getParent(popup) == invoker) {
182: return popup;
183: }
184: }
185: }
186: return null;
187: }
188:
189: /** Return the popup descendent of the given known transient popup
190: * container.
191: */
192: private JComponent findPopup(Container c) {
193: JComponent popup = null;
194: Component[] kids = c.getComponents();
195: for (int i = 0; i < kids.length && popup == null; i++) {
196: if (kids[i] instanceof JPopupMenu
197: || kids[i] instanceof JToolTip) {
198: popup = (JComponent) kids[i];
199: } else if (kids[i] instanceof Container) {
200: popup = findPopup((Container) kids[i]);
201: }
202: }
203: return popup;
204: }
205: }
|