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.multiview;
043:
044: import java.awt.BorderLayout;
045: import java.awt.Color;
046: import java.awt.FlowLayout;
047: import java.beans.PropertyChangeEvent;
048: import java.beans.PropertyChangeListener;
049: import java.io.IOException;
050: import java.util.Arrays;
051: import javax.swing.ActionMap;
052: import javax.swing.JPanel;
053: import javax.swing.JSeparator;
054: import javax.swing.SwingConstants;
055: import javax.swing.SwingUtilities;
056: import javax.swing.UIManager;
057: import javax.swing.text.DefaultEditorKit;
058: import org.netbeans.core.spi.multiview.CloseOperationState;
059: import org.netbeans.core.spi.multiview.MultiViewElement;
060: import org.netbeans.core.spi.multiview.MultiViewElementCallback;
061: import org.netbeans.core.spi.multiview.MultiViewFactory;
062: import org.netbeans.modules.xml.axi.AXIModel;
063: import org.netbeans.modules.xml.axi.AXIModelFactory;
064: import org.netbeans.modules.xml.schema.abe.InstanceDesignerPanel;
065: import org.netbeans.modules.xml.schema.abe.UIUtilities;
066: import org.netbeans.modules.xml.schema.SchemaDataObject;
067: import org.netbeans.modules.xml.schema.SchemaEditorSupport;
068: import org.netbeans.modules.xml.schema.model.SchemaModel;
069: import org.netbeans.modules.xml.validation.ShowCookie;
070: import org.netbeans.modules.xml.xam.Component;
071: import org.netbeans.modules.xml.xam.Model.State;
072: import org.netbeans.modules.xml.xam.spi.Validator.ResultItem;
073: import org.netbeans.modules.xml.xam.ui.multiview.ActivatedNodesMediator;
074: import org.netbeans.modules.xml.xam.ui.multiview.CookieProxyLookup;
075: import org.netbeans.modules.xml.xam.ui.undo.QuietUndoManager;
076: import org.openide.util.Lookup;
077: import org.openide.windows.TopComponent;
078: import org.openide.awt.UndoRedo;
079: import org.openide.explorer.ExplorerManager;
080: import org.openide.explorer.ExplorerUtils;
081: import org.openide.nodes.Node;
082: import org.openide.util.HelpCtx;
083: import org.openide.util.NbBundle;
084: import org.openide.util.WeakListeners;
085: import org.openide.util.lookup.Lookups;
086:
087: /**
088: *
089: * @author Jeri Lockhart
090: */
091: public class SchemaABEViewMultiViewElement extends TopComponent
092: implements MultiViewElement, PropertyChangeListener,
093: ExplorerManager.Provider {
094:
095: private static final long serialVersionUID = -483941387931729295L;
096: private AXIModel axiModel;
097: private String errorMessage;
098: private SchemaDataObject schemaDataObject;
099: private InstanceDesignerPanel abeDesigner;
100: private transient JPanel toolBarPanel;
101: private javax.swing.JLabel errorLabel = new javax.swing.JLabel();
102: private transient MultiViewElementCallback multiViewCallback;
103: private ExplorerManager manager;
104:
105: public SchemaABEViewMultiViewElement() {
106: super ();
107: // For deserialization only
108: }
109:
110: public SchemaABEViewMultiViewElement(
111: SchemaDataObject schemaDataObject) {
112: super ();
113: this .schemaDataObject = schemaDataObject;
114: initialize();
115: }
116:
117: public void propertyChange(PropertyChangeEvent evt) {
118: String property = evt.getPropertyName();
119: if (!AXIModel.STATE_PROPERTY.equals(property)) {
120: return;
121: }
122: State newState = (State) evt.getNewValue();
123: if (newState == AXIModel.State.VALID) {
124: errorMessage = null;
125: recreateUI();
126: return;
127: }
128:
129: if (errorMessage == null)
130: errorMessage = NbBundle.getMessage(
131: SchemaColumnViewMultiViewElement.class,
132: "MSG_InvalidSchema");
133: //fix for IZ:116057
134: SwingUtilities.invokeLater(new Runnable() {
135: public void run() {
136: setActivatedNodes(new Node[] { schemaDataObject
137: .getNodeDelegate() });
138: }
139: });
140: emptyUI(errorMessage);
141: }
142:
143: public ExplorerManager getExplorerManager() {
144: return manager;
145: }
146:
147: private void initialize() {
148: // Place the palette controller and some other things into the lookup
149: Node delegate = schemaDataObject.getNodeDelegate();
150:
151: //show cookie
152: ShowCookie showCookie = new ShowCookie() {
153:
154: public void show(ResultItem resultItem) {
155: Component component = resultItem.getComponents();
156: if (component != null) {
157: abeDesigner.selectUIComponent(component);
158: }
159: }
160: };
161:
162: ActivatedNodesMediator nodesMediator = new ActivatedNodesMediator(
163: delegate);
164: manager = new ExplorerManager();
165: nodesMediator.setExplorerManager(this );
166: ActionMap map = getActionMap();
167: map.put(DefaultEditorKit.copyAction, ExplorerUtils
168: .actionCopy(manager));
169: map.put(DefaultEditorKit.cutAction, ExplorerUtils
170: .actionCut(manager));
171: map.put(DefaultEditorKit.pasteAction, ExplorerUtils
172: .actionPaste(manager));
173: map.put("delete", ExplorerUtils.actionDelete(manager, false));
174:
175: CookieProxyLookup cpl = new CookieProxyLookup(new Lookup[] {
176: nodesMediator.getLookup(),
177: // use a proxy lookup here as the abeDesigner is only
178: // active
179: Lookups.proxy(new Lookup.Provider() {
180: public Lookup getLookup() {
181: Lookup lookup = Lookup.EMPTY;
182: if (abeDesigner != null) {
183: lookup = Lookups.singleton(abeDesigner
184: .getPaletteController());
185: }
186: return lookup;
187: }
188: }), Lookups.fixed(new Object[] {
189: // Need the action map in our custom lookup so actions work.
190: getActionMap(),
191: // Need the data object registered in the lookup so that the
192: // projectui code will close our open editor windows when the
193: // project is closed.
194: schemaDataObject,
195: // The Show Cookie in lookup to show schema component
196: showCookie, }),
197: // The Node delegate Lookup must be the last one in the list
198: // for the CookieProxyLookup to work properly.
199: delegate.getLookup(), }, delegate);
200: associateLookup(cpl);
201: addPropertyChangeListener("activatedNodes", nodesMediator);
202: addPropertyChangeListener("activatedNodes", cpl);
203: //initUI();
204: }
205:
206: /**
207: * Initializes the UI. Here it checks for the state of the underlying
208: * schema model. If valid, draws the UI, else empties the UI with proper
209: * error message.
210: */
211: boolean firsTime = true;
212:
213: private void initUI() {
214: if (firsTime) {
215: setLayout(new BorderLayout());
216: //initialize the error label one time.
217: errorLabel
218: .setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
219: errorLabel
220: .setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
221: Color usualWindowBkg = UIManager.getColor("window"); //NOI18N
222: errorLabel
223: .setBackground(usualWindowBkg != null ? usualWindowBkg
224: : Color.white);
225: errorLabel.setOpaque(true);
226: firsTime = false;
227: }
228:
229: AXIModel model = getAXIModel();
230: if ((model != null)
231: && (model.getState() == AXIModel.State.VALID)) {
232: recreateUI();
233: return;
234: }
235:
236: //if it comes here, either the schema is not well-formed or invalid
237: if (errorMessage == null)
238: errorMessage = NbBundle.getMessage(
239: SchemaColumnViewMultiViewElement.class,
240: "MSG_InvalidSchema");
241: emptyUI(errorMessage);
242: }
243:
244: /**
245: * Creates the UI. Creates the abeDesigner only if it was null
246: * or the underlying documnet was found modifed externally.
247: */
248: boolean propChangeListenerAdded = false;
249:
250: private void recreateUI() {
251: if (abeDesigner == null) {
252: abeDesigner = new InstanceDesignerPanel(getAXIModel(),
253: schemaDataObject, this );
254: }
255: // Add the ABE designer panel
256: if (!isChild(abeDesigner)) {
257: add(abeDesigner, BorderLayout.CENTER);
258: }
259: if (!propChangeListenerAdded) {
260: abeDesigner.addPropertyChangeListener(this );
261: propChangeListenerAdded = true;
262: }
263: if (errorLabel != null)
264: errorLabel.setVisible(false);
265: abeDesigner.setVisible(true);
266: revalidate();
267: repaint();
268: }
269:
270: /**
271: * Empties the UI, with proper error message, when the underlying
272: * schema model is in INVALID/NOT-WELLFORMED state.
273: */
274:
275: private void emptyUI(String errorMessage) {
276: if (abeDesigner != null)
277: abeDesigner.setVisible(false);
278: errorLabel.setText("<" + errorMessage + ">");
279: if (!isChild(errorLabel))
280: add(errorLabel, BorderLayout.NORTH);
281: if (propChangeListenerAdded) {
282: abeDesigner.addPropertyChangeListener(this );
283: propChangeListenerAdded = false;
284: }
285: errorLabel.setVisible(true);
286: revalidate();
287: repaint();
288: }
289:
290: private boolean isChild(java.awt.Component comp) {
291: java.awt.Component compArry[] = getComponents();
292: if ((compArry == null) || (compArry.length <= 0)) {
293: return false;
294: }
295: return Arrays.asList(compArry).contains(comp);
296: }
297:
298: /////////////////////////////////////////////////////////////////////////////
299: // MultiViewElement implementation
300: /////////////////////////////////////////////////////////////////////////////
301:
302: /**
303: *
304: *
305: */
306: public int getPersistenceType() {
307: return PERSISTENCE_NEVER;
308: }
309:
310: public void setMultiViewCallback(MultiViewElementCallback callback) {
311: multiViewCallback = callback;
312: }
313:
314: public HelpCtx getHelpCtx() {
315: return new HelpCtx(SchemaABEViewMultiViewDesc.class);
316: }
317:
318: public CloseOperationState canCloseElement() {
319: // if this is not the last cloned xml editor component, closing is OK
320: if (!SchemaMultiViewSupport.isLastView(multiViewCallback
321: .getTopComponent())) {
322: return CloseOperationState.STATE_OK;
323: }
324: // return a placeholder state - to be sure our CloseHandler is called
325: return MultiViewFactory.createUnsafeCloseState(
326: "ID_SCHEMA_ABEVIEW_CLOSING", // dummy ID // NOI18N
327: MultiViewFactory.NOOP_CLOSE_ACTION,
328: MultiViewFactory.NOOP_CLOSE_ACTION);
329: }
330:
331: /**
332: *
333: *
334: */
335: public void componentActivated() {
336: super .componentActivated();
337: ExplorerUtils.activateActions(manager, true);
338: addUndoManager();
339: UIUtilities.hideGlassMessage(true);
340: }
341:
342: /**
343: *
344: *
345: */
346: public void componentClosed() {
347: super .componentClosed();
348: UIUtilities.hideGlassMessage(true);
349: if (abeDesigner != null) {
350: abeDesigner.shutdown();
351: abeDesigner = null;
352: }
353: removeUndoManager();
354: this .removeAll();
355: this .setLayout(null);
356: //associateLookup(Lookup.EMPTY);
357: }
358:
359: /**
360: *
361: *
362: */
363: public void componentDeactivated() {
364: super .componentDeactivated();
365: ExplorerUtils.activateActions(manager, false);
366: UIUtilities.hideGlassMessage(true);
367: }
368:
369: /**
370: *
371: *
372: */
373: public void componentHidden() {
374: super .componentHidden();
375: UIUtilities.hideGlassMessage(true);
376: }
377:
378: /**
379: *
380: *
381: */
382: public void componentOpened() {
383: super .componentOpened();
384: UIUtilities.hideGlassMessage(true);
385: }
386:
387: /**
388: *
389: *
390: */
391: public void componentShowing() {
392: super .componentShowing();
393: initUI();
394: addUndoManager();
395: UIUtilities.hideGlassMessage(true);
396: }
397:
398: @SuppressWarnings("deprecation")
399: public void requestFocus() {
400: super .requestFocus();
401: // For Help to work properly, need to take focus.
402: if (abeDesigner != null) {
403: abeDesigner.requestFocus();
404: }
405: }
406:
407: @SuppressWarnings("deprecation")
408: public boolean requestFocusInWindow() {
409: boolean retVal = super .requestFocusInWindow();
410: // For Help to work properly, need to take focus.
411: if (abeDesigner != null) {
412: return abeDesigner.requestFocusInWindow();
413: }
414: return retVal;
415: }
416:
417: /**
418: *
419: *
420: */
421: public javax.swing.JComponent getToolbarRepresentation() {
422: if (toolBarPanel == null) {
423: toolBarPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
424:
425: //dummy panel for spacing
426: //toolBarPanel.add(new JPanel(), BorderLayout.);
427:
428: JSeparator jsep = new JSeparator(SwingConstants.VERTICAL);
429: toolBarPanel.add(jsep);
430:
431: }
432: return toolBarPanel;
433: }
434:
435: /**
436: * Adds the undo/redo manager to the schema model as an undoable
437: * edit listener, so it receives the edits onto the queue.
438: */
439: private void addUndoManager() {
440: SchemaModel model = getAXIModel().getSchemaModel();
441: if (model != null) {
442: SchemaEditorSupport editor = schemaDataObject
443: .getSchemaEditorSupport();
444: QuietUndoManager undo = editor.getUndoManager();
445: // Ensure the listener is not added twice.
446: model.removeUndoableEditListener(undo);
447: model.addUndoableEditListener(undo);
448: // Ensure the model is sync'd when undo/redo is invoked,
449: // otherwise the edits are added to the queue and eventually
450: // cause exceptions.
451: undo.setModel(model);
452: undo.addWrapperModel(getAXIModel());
453: }
454: }
455:
456: private void removeUndoManager() {
457: SchemaModel model = getAXIModel().getSchemaModel();
458: if (model != null) {
459: SchemaEditorSupport editor = schemaDataObject
460: .getSchemaEditorSupport();
461: QuietUndoManager undo = editor.getUndoManager();
462: model.removeUndoableEditListener(undo);
463: undo.removeWrapperModel(model);
464: }
465: }
466:
467: /**
468: *
469: *
470: */
471: public UndoRedo getUndoRedo() {
472: return schemaDataObject.getSchemaEditorSupport()
473: .getUndoManager();
474: }
475:
476: private AXIModel getAXIModel() {
477: if (axiModel != null) {
478: return axiModel;
479: }
480: try {
481: SchemaModel sModel = schemaDataObject
482: .getSchemaEditorSupport().getModel();
483: axiModel = AXIModelFactory.getDefault().getModel(sModel);
484: if (axiModel != null) {
485: PropertyChangeListener pcl = WeakListeners.create(
486: PropertyChangeListener.class, this , axiModel);
487: axiModel.addPropertyChangeListener(pcl);
488: }
489: } catch (IOException e) {
490: errorMessage = e.getMessage();
491: }
492: return axiModel;
493: }
494:
495: /**
496: *
497: *
498: */
499: public javax.swing.JComponent getVisualRepresentation() {
500: return this;
501: }
502:
503: }
|