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:
042: package org.netbeans.modules.xml.schema.ui.basic.navigator;
043:
044: import java.awt.BorderLayout;
045: import java.awt.Color;
046: import java.awt.EventQueue;
047: import java.beans.PropertyChangeEvent;
048: import java.beans.PropertyVetoException;
049: import java.io.IOException;
050: import java.util.ArrayList;
051: import java.util.Enumeration;
052: import java.util.List;
053: import javax.swing.JTree;
054: import javax.swing.SwingConstants;
055: import javax.swing.SwingUtilities;
056: import javax.swing.UIManager;
057: import org.netbeans.modules.xml.schema.model.SchemaComponent;
058: import org.netbeans.modules.xml.schema.model.SchemaModel;
059: import org.netbeans.modules.xml.schema.ui.basic.SchemaModelCookie;
060: import org.netbeans.modules.xml.schema.ui.basic.UIUtilities;
061: import org.netbeans.modules.xml.schema.ui.nodes.DefaultExpandedCookie;
062: import org.netbeans.modules.xml.schema.ui.nodes.SchemaNodeFactory;
063: import org.netbeans.modules.xml.schema.ui.nodes.ReadOnlyCookie;
064: import org.netbeans.modules.xml.schema.ui.nodes.categorized.CategorizedSchemaNodeFactory;
065: import org.netbeans.modules.xml.xam.Model.State;
066: import org.openide.explorer.ExplorerManager;
067: import org.openide.loaders.DataObject;
068: import org.openide.nodes.Node;
069: import org.openide.util.Lookup;
070: import org.openide.util.NbBundle;
071: import org.openide.util.lookup.Lookups;
072: import org.openide.windows.TopComponent;
073: import org.netbeans.modules.xml.text.navigator.base.AbstractXMLNavigatorContent;
074:
075: /**
076: * XML Schema Navigator component containing a tree of schema components.
077: *
078: * @author Nathan Fiedler
079: */
080: public class SchemaNavigatorContent extends AbstractXMLNavigatorContent
081: implements Runnable {
082: /** silence compiler warnings */
083: private static final long serialVersionUID = 1L;
084: /** The lookup for our component tree. */
085: private static Lookup lookup;
086: /** Indicates that the tree view is not in the component hierarchy. */
087: private boolean treeInHierarchy;
088: /** indicator that currently listening to topcomponent.registry.activatednodes **/
089: private boolean listeningOnActivatedNodes = false;
090: /** Explorer root node **/
091: private Node explorerRoot;
092: private final javax.swing.JLabel notAvailableLabel = new javax.swing.JLabel(
093: NbBundle.getMessage(SchemaNavigatorContent.class,
094: "MSG_NotAvailable")); //NOI18N
095:
096: static {
097: // Present a read-only view of the schema components.
098: lookup = Lookups.singleton(new ReadOnlyCookie(true));
099: }
100:
101: /**
102: * Creates a new instance of SchemaNavigatorContent.
103: */
104: public SchemaNavigatorContent() {
105: super ();
106: setLayout(new BorderLayout());
107: //initialize the notAvailableLabel
108: notAvailableLabel.setHorizontalAlignment(SwingConstants.CENTER);
109: notAvailableLabel.setEnabled(false);
110: Color usualWindowBkg = UIManager.getColor("window"); //NOI18N
111: notAvailableLabel
112: .setBackground(usualWindowBkg != null ? usualWindowBkg
113: : Color.white);
114: // to ensure our background color will have effect
115: notAvailableLabel.setOpaque(true);
116: }
117:
118: /**
119: * Expand the nodes which should be expanded by default.
120: */
121: protected void expandDefaultNodes() {
122: Node rootNode = getExplorerManager().getRootContext();
123: // Need to prevent looping on malformed trees, so avoid going too
124: // deep when expanding the children of nodes with only one child.
125: int depth = 0;
126: do {
127: Node[] children = rootNode.getChildren().getNodes();
128: if (children.length == 1) {
129: // Expand all nodes that have only a single child.
130: treeView.expandNode(children[0]);
131: rootNode = children[0];
132: depth++;
133: } else {
134: // Expand all first-level children that are meant to be shown
135: // expanded by default.
136: for (Node child : children) {
137: DefaultExpandedCookie cookie = (DefaultExpandedCookie) child
138: .getCookie(DefaultExpandedCookie.class);
139: if (cookie != null && cookie.isDefaultExpanded()) {
140: treeView.expandNode(child);
141: }
142: }
143: rootNode = null;
144: }
145: } while (rootNode != null && depth < 5);
146:
147: // The following code addresses two issues:
148: //
149: // 1. When viewing large schemas, expanding the default set of nodes
150: // generally means that the contents of the column are so long that
151: // copious amounts of scrolling are necessary to see it all. This is
152: // not desirable for the user's first experience with the document.
153: //
154: // 2. Because BasicTreeUI essentially ignores the scrollsOnExpand
155: // setting (or at least it does not work as documented), the tree
156: // is left scrolled to some random position.
157: //
158: // So, if scrolling is necessary, then collapse root's children.
159: JTree tree = (JTree) treeView.getViewport().getView();
160: if (tree.getRowCount() > tree.getVisibleRowCount()) {
161: rootNode = getExplorerManager().getRootContext();
162: Enumeration kids = rootNode.getChildren().nodes();
163: while (kids.hasMoreElements()) {
164: Node kid = (Node) kids.nextElement();
165: treeView.collapseNode(kid);
166: }
167: }
168: }
169:
170: public ExplorerManager getExplorerManager() {
171: return explorerManager;
172: }
173:
174: private SchemaModel getSchemaModel(DataObject dobj) {
175: try {
176: SchemaModelCookie modelCookie = dobj
177: .getCookie(SchemaModelCookie.class);
178: //it is possible that the dobj is no longer for a schema.
179: if (modelCookie == null)
180: return null;
181: SchemaModel model = modelCookie.getModel();
182: if (model != null) {
183: model.removePropertyChangeListener(this );
184: model.addPropertyChangeListener(this );
185: }
186: return model;
187: } catch (IOException ioe) {
188: //will show blank page if there is an error.
189: }
190:
191: return null;
192: }
193:
194: /**
195: * Show the data object in the navigator.
196: *
197: * @param dobj data object to show.
198: */
199: public void navigate(DataObject dobj) {
200: SchemaModel model = getSchemaModel(dobj);
201: if (model == null
202: || model.getState() != SchemaModel.State.VALID) {
203: showError();
204: } else {
205: show(model);
206: }
207: }
208:
209: public boolean requestFocusInWindow() {
210: return treeView.requestFocusInWindow();
211: }
212:
213: public void run() {
214: getExplorerManager().setRootContext(explorerRoot);
215: expandDefaultNodes();
216: selectActivatedNodes();
217: }
218:
219: public void propertyChange(PropertyChangeEvent event) {
220: String property = event.getPropertyName();
221: if (SchemaModel.STATE_PROPERTY.equals(property)) {
222: onModelStateChanged(event);
223: return;
224: }
225: TopComponent tc = (TopComponent) SwingUtilities
226: .getAncestorOfClass(TopComponent.class, this );
227: if (ExplorerManager.PROP_SELECTED_NODES.equals(property)
228: && tc == TopComponent.getRegistry().getActivated()) {
229: Node[] filteredNodes = (Node[]) event.getNewValue();
230: if (filteredNodes != null && filteredNodes.length >= 1) {
231: // Set the active nodes for the parent TopComponent.
232: tc.setActivatedNodes(filteredNodes);
233: }
234: } else if (TopComponent.getRegistry().PROP_ACTIVATED_NODES
235: .equals(property)
236: && tc != null
237: && tc != TopComponent.getRegistry().getActivated()) {
238: EventQueue.invokeLater(new Runnable() {
239: public void run() {
240: selectActivatedNodes();
241: }
242: });
243: } else if (TopComponent.getRegistry().PROP_ACTIVATED
244: .equals(property)
245: && tc == TopComponent.getRegistry().getActivated()) {
246: tc.setActivatedNodes(getExplorerManager()
247: .getSelectedNodes());
248: }
249: }
250:
251: private void selectActivatedNodes() {
252: Node[] activated = TopComponent.getRegistry()
253: .getActivatedNodes();
254: List<Node> selNodes = new ArrayList<Node>();
255: for (Node n : activated) {
256: SchemaComponent sc = (SchemaComponent) n.getLookup()
257: .lookup(SchemaComponent.class);
258: if (sc != null) {
259: List<Node> path = UIUtilities.findPathFromRoot(
260: getExplorerManager().getRootContext(), sc);
261: if (path != null && !path.isEmpty())
262: selNodes.add(path.get(path.size() - 1));
263: }
264: }
265: try {
266: getExplorerManager().setSelectedNodes(
267: selNodes.toArray(new Node[0]));
268: } catch (PropertyVetoException ex) {
269: }
270: }
271:
272: public void onModelStateChanged(PropertyChangeEvent evt) {
273: State newState = (State) evt.getNewValue();
274: if (newState == SchemaModel.State.VALID) {
275: SchemaModel model = (SchemaModel) evt.getSource();
276: show(model);
277: return;
278: }
279:
280: //model is broken
281: showError();
282: return;
283: }
284:
285: private void showError() {
286: if (notAvailableLabel.isShowing()) {
287: return;
288: }
289: remove(treeView);
290: add(notAvailableLabel, BorderLayout.CENTER);
291: revalidate();
292: repaint();
293: }
294:
295: private void show(SchemaModel model) {
296: remove(notAvailableLabel);
297: add(treeView, BorderLayout.CENTER);
298: SchemaNodeFactory factory = new CategorizedSchemaNodeFactory(
299: model, lookup);
300: explorerRoot = factory.createRootNode();
301: // Expand the default nodes.
302: EventQueue.invokeLater(this);
303: revalidate();
304: repaint();
305: }
306: }
|