0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.form;
0043:
0044: import java.awt.EventQueue;
0045: import java.beans.PropertyChangeEvent;
0046: import java.beans.PropertyChangeListener;
0047: import java.io.IOException;
0048: import java.io.InputStream;
0049: import java.io.ObjectInput;
0050: import java.io.ObjectOutput;
0051: import java.io.OutputStream;
0052: import java.io.Reader;
0053: import java.io.Serializable;
0054: import java.io.Writer;
0055: import java.lang.reflect.InvocationTargetException;
0056: import java.nio.charset.Charset;
0057: import java.text.MessageFormat;
0058: import java.util.Collections;
0059: import java.util.Enumeration;
0060: import java.util.HashMap;
0061: import java.util.HashSet;
0062: import java.util.Iterator;
0063: import java.util.Map;
0064: import java.util.Set;
0065: import java.util.logging.Level;
0066: import java.util.logging.Logger;
0067: import javax.swing.JComponent;
0068: import javax.swing.JEditorPane;
0069: import javax.swing.JPanel;
0070: import javax.swing.text.BadLocationException;
0071: import javax.swing.text.Document;
0072: import javax.swing.text.EditorKit;
0073: import javax.swing.text.Position;
0074: import javax.swing.text.StyledDocument;
0075: import org.netbeans.api.editor.guards.GuardedSectionManager;
0076: import org.netbeans.api.editor.guards.SimpleSection;
0077: import org.netbeans.api.queries.FileEncodingQuery;
0078: import org.netbeans.core.api.multiview.MultiViewHandler;
0079: import org.netbeans.core.api.multiview.MultiViews;
0080: import org.netbeans.core.spi.multiview.CloseOperationHandler;
0081: import org.netbeans.core.spi.multiview.CloseOperationState;
0082: import org.netbeans.core.spi.multiview.MultiViewDescription;
0083: import org.netbeans.core.spi.multiview.MultiViewElement;
0084: import org.netbeans.core.spi.multiview.MultiViewElementCallback;
0085: import org.netbeans.core.spi.multiview.MultiViewFactory;
0086: import org.netbeans.core.spi.multiview.SourceViewMarker;
0087: import org.netbeans.spi.editor.guards.GuardedEditorSupport;
0088: import org.netbeans.spi.editor.guards.GuardedSectionsFactory;
0089: import org.netbeans.spi.editor.guards.GuardedSectionsProvider;
0090: import org.openide.DialogDisplayer;
0091: import org.openide.ErrorManager;
0092: import org.openide.NotifyDescriptor;
0093: import org.openide.awt.UndoRedo;
0094: import org.openide.cookies.CloseCookie;
0095: import org.openide.cookies.EditorCookie;
0096: import org.openide.cookies.PrintCookie;
0097: import org.openide.cookies.SaveCookie;
0098: import org.openide.filesystems.FileLock;
0099: import org.openide.filesystems.FileObject;
0100: import org.openide.filesystems.FileStateInvalidException;
0101: import org.openide.filesystems.FileStatusEvent;
0102: import org.openide.filesystems.FileStatusListener;
0103: import org.openide.filesystems.FileSystem;
0104: import org.openide.filesystems.FileUtil;
0105: import org.openide.loaders.DataObject;
0106: import org.openide.loaders.MultiDataObject;
0107: import org.openide.nodes.CookieSet;
0108: import org.openide.nodes.Node;
0109: import org.openide.text.CloneableEditor;
0110: import org.openide.text.CloneableEditorSupport;
0111: import org.openide.text.DataEditorSupport;
0112: import org.openide.text.NbDocument;
0113: import org.openide.text.PositionRef;
0114: import org.openide.util.UserQuestionException;
0115: import org.openide.util.Utilities;
0116: import org.openide.windows.CloneableOpenSupport;
0117: import org.openide.windows.CloneableTopComponent;
0118: import org.openide.windows.Mode;
0119: import org.openide.windows.TopComponent;
0120: import org.openide.windows.TopComponentGroup;
0121: import org.openide.windows.WindowManager;
0122:
0123: /**
0124: *
0125: * @author Ian Formanek, Tomas Pavek
0126: */
0127:
0128: public class FormEditorSupport extends DataEditorSupport implements
0129: EditorCookie.Observable, CloseCookie, PrintCookie {
0130:
0131: /** ID of the form designer (in the multiview) */
0132: private static final String MV_FORM_ID = "form"; //NOI18N
0133: /** ID of the java editor (in the multiview) */
0134: private static final String MV_JAVA_ID = "java"; // NOI18N
0135:
0136: private static final int JAVA_ELEMENT_INDEX = 0;
0137: private static final int FORM_ELEMENT_INDEX = 1;
0138: private int elementToOpen; // default element index when multiview TC is created
0139:
0140: private static final String SECTION_INIT_COMPONENTS = "initComponents"; // NOI18N
0141: private static final String SECTION_VARIABLES = "variables"; // NOI18N
0142:
0143: /** Icon for the form editor multiview window */
0144: private static final String iconURL = "org/netbeans/modules/form/resources/form.gif"; // NOI18N
0145:
0146: /** The DataObject of the form */
0147: private FormDataObject formDataObject;
0148:
0149: /** The embracing multiview TopComponent (holds the form designer and
0150: * java editor) - we remeber the last active TopComponent (not all clones) */
0151: private CloneableTopComponent multiviewTC;
0152:
0153: // listeners
0154: private static PropertyChangeListener topcompsListener;
0155:
0156: private UndoRedo.Manager editorUndoManager;
0157:
0158: private FormEditor formEditor;
0159:
0160: /** Set of opened FormEditorSupport instances (java or form opened) */
0161: private static Set<FormEditorSupport> opened = Collections
0162: .synchronizedSet(new HashSet<FormEditorSupport>());
0163:
0164: private static Map<FileSystem, FileStatusListener> fsToStatusListener = new HashMap<FileSystem, FileStatusListener>();
0165:
0166: // --------------
0167: // constructor
0168:
0169: public FormEditorSupport(MultiDataObject.Entry javaEntry,
0170: FormDataObject formDataObject, CookieSet cookies) {
0171: super (formDataObject, new Environment(formDataObject));
0172: setMIMEType("text/x-java"); // NOI18N
0173: this .formDataObject = formDataObject;
0174: this .cookies = cookies;
0175: }
0176:
0177: // ----------
0178: // opening & saving interface methods
0179:
0180: /** Main entry method. Called by OpenCookie implementation - opens the form.
0181: *
0182: * @param forceFormElement determines whether we should force switch to form element.
0183: * @see OpenCookie#open
0184: */
0185: public void openFormEditor(boolean forceFormElement) {
0186: boolean alreadyOpened = opened.contains(this );
0187: boolean switchToForm = forceFormElement || !alreadyOpened;
0188: if (switchToForm) {
0189: elementToOpen = FORM_ELEMENT_INDEX;
0190: }
0191: multiviewTC = openCloneableTopComponent();
0192: multiviewTC.requestActive();
0193:
0194: if (switchToForm) {
0195: MultiViewHandler handler = MultiViews
0196: .findMultiViewHandler(multiviewTC);
0197: handler
0198: .requestActive(handler.getPerspectives()[FORM_ELEMENT_INDEX]);
0199: }
0200: }
0201:
0202: private void addStatusListener(FileSystem fs) {
0203: FileStatusListener fsl = fsToStatusListener.get(fs);
0204: if (fsl == null) {
0205: fsl = new FileStatusListener() {
0206: public void annotationChanged(FileStatusEvent ev) {
0207: Iterator<FormEditorSupport> iter = opened
0208: .iterator();
0209: while (iter.hasNext()) {
0210: FormEditorSupport fes = iter.next();
0211: if (ev.hasChanged(fes.getFormDataObject()
0212: .getPrimaryFile())
0213: || ev.hasChanged(fes
0214: .getFormDataObject()
0215: .getFormFile())) {
0216: fes.updateMVTCDisplayName();
0217: }
0218: }
0219: }
0220: };
0221: fs.addFileStatusListener(fsl);
0222: fsToStatusListener.put(fs, fsl);
0223: } // else do nothing - the listener is already added
0224: }
0225:
0226: private static void detachStatusListeners() {
0227: Iterator iter = fsToStatusListener.entrySet().iterator();
0228: while (iter.hasNext()) {
0229: Map.Entry entry = (Map.Entry) iter.next();
0230: FileSystem fs = (FileSystem) entry.getKey();
0231: FileStatusListener fsl = (FileStatusListener) entry
0232: .getValue();
0233: fs.removeFileStatusListener(fsl);
0234: }
0235: fsToStatusListener.clear();
0236: }
0237:
0238: void selectJavaEditor() {
0239: MultiViewHandler handler = MultiViews
0240: .findMultiViewHandler(multiviewTC);
0241: if (handler != null) {
0242: handler
0243: .requestActive(handler.getPerspectives()[JAVA_ELEMENT_INDEX]);
0244: }
0245: }
0246:
0247: /** Overriden from JavaEditor - opens editor and ensures it is selected
0248: * in the multiview.
0249: */
0250: @Override
0251: public void open() {
0252: if (EventQueue.isDispatchThread()) {
0253: openInAWT();
0254: } else {
0255: EventQueue.invokeLater(new Runnable() {
0256: public void run() {
0257: openInAWT();
0258: }
0259: });
0260: }
0261: }
0262:
0263: private void openInAWT() {
0264: if (!formDataObject.isValid()) {
0265: return;
0266: }
0267: if (Boolean.TRUE.equals(formDataObject.getPrimaryFile()
0268: .getAttribute("nonEditableTemplate"))) { // NOI18N
0269: String pattern = FormUtils
0270: .getBundleString("MSG_NonEditableTemplate"); // NOI18N
0271: String message = MessageFormat.format(pattern,
0272: new Object[] { formDataObject.getNodeDelegate()
0273: .getName() });
0274: DialogDisplayer.getDefault().notify(
0275: new NotifyDescriptor.Message(message));
0276: return;
0277: }
0278: elementToOpen = JAVA_ELEMENT_INDEX;
0279: super .open();
0280:
0281: // This method must be executed in AWT thread because
0282: // otherwise multiview is opened in AWT using invokeLater
0283: // and we don't have multiviewTC correctly set
0284: MultiViewHandler handler = MultiViews
0285: .findMultiViewHandler(multiviewTC);
0286: handler
0287: .requestActive(handler.getPerspectives()[JAVA_ELEMENT_INDEX]);
0288: }
0289:
0290: /** Overriden from JavaEditor - opens editor at given position and ensures
0291: * it is selected in the multiview.
0292: *
0293: * @param pos position
0294: */
0295: public void openAt(PositionRef pos) {
0296: elementToOpen = JAVA_ELEMENT_INDEX;
0297: openCloneableTopComponent();
0298:
0299: MultiViewHandler handler = MultiViews
0300: .findMultiViewHandler(multiviewTC);
0301: handler
0302: .requestActive(handler.getPerspectives()[JAVA_ELEMENT_INDEX]);
0303:
0304: openAt(pos, -1).getComponent().requestActive();
0305: }
0306:
0307: public void openAt(Position pos) {
0308: openAt(createPositionRef(pos.getOffset(), Position.Bias.Forward));
0309: }
0310:
0311: /** Public method for loading form data from file. Does not open the
0312: * source editor and designer, does not report errors and does not throw
0313: * any exceptions. Runs in AWT event dispatch thread, returns after the
0314: * form is loaded (even if not called from AWT thread).
0315: * @return whether the form is loaded (true also if it already was)
0316: */
0317: public boolean loadForm() {
0318: // Ensure initialization of the formEditor
0319: getFormEditor(true);
0320: return formEditor.loadForm();
0321: }
0322:
0323: /** @return true if the form is opened, false otherwise */
0324: public boolean isOpened() {
0325: return (formEditor != null) && formEditor.isFormLoaded();
0326: }
0327:
0328: private boolean saving; // workaround for bug 75225
0329:
0330: /** Save the document in this thread and start reparsing it.
0331: * @exception IOException on I/O error
0332: */
0333: @Override
0334: public void saveDocument() throws IOException {
0335: IOException ioEx = null;
0336: try {
0337: if (formEditor != null) {
0338: formEditor.saveFormData();
0339: }
0340: saving = true; // workaround for bug 75225
0341: super .saveDocument();
0342: } catch (PersistenceException ex) {
0343: Throwable t = ex.getOriginalException();
0344: if (t instanceof IOException)
0345: ioEx = (IOException) t;
0346: else {
0347: ioEx = new IOException("Cannot save the form"); // NOI18N
0348: ErrorManager.getDefault().annotate(ioEx,
0349: t != null ? t : ex);
0350: }
0351: } finally {
0352: saving = false; // workaround for bug 75225
0353: }
0354: if (formEditor != null) {
0355: formEditor.reportErrors(FormEditor.SAVING);
0356: }
0357:
0358: if (ioEx != null)
0359: throw ioEx;
0360: }
0361:
0362: void saveSourceOnly() throws IOException {
0363: try {
0364: saving = true; // workaround for bug 75225
0365: super .saveDocument();
0366: } finally {
0367: saving = false; // workaround for bug 75225
0368: }
0369: }
0370:
0371: // ------------
0372: // other interface methods
0373:
0374: /** @return data object representing the form */
0375: public final FormDataObject getFormDataObject() {
0376: return formDataObject;
0377: }
0378:
0379: // PENDING remove when form_new_layout is merged to trunk
0380: public static FormDataObject getFormDataObject(FormModel formModel) {
0381: return FormEditor.getFormDataObject(formModel);
0382: }
0383:
0384: public FormModel getFormModel() {
0385: FormEditor fe = getFormEditor();
0386: return (fe == null) ? null : fe.getFormModel();
0387: }
0388:
0389: // END of PENDING
0390:
0391: public FormEditor getFormEditor() {
0392: return getFormEditor(false);
0393: }
0394:
0395: FormEditor getFormEditor(boolean initialize) {
0396: if ((formEditor == null) && initialize) {
0397: formEditor = new FormEditor(formDataObject);
0398: }
0399: return formEditor;
0400: }
0401:
0402: /** Marks the form as modified if it's not yet. Used if changes made
0403: * in form data don't affect the java source file (generated code). */
0404: void markFormModified() {
0405: if (formEditor != null && formEditor.isFormLoaded()
0406: && !formDataObject.isModified()) {
0407: notifyModified();
0408: }
0409: }
0410:
0411: @Override
0412: protected UndoRedo.Manager createUndoRedoManager() {
0413: editorUndoManager = super .createUndoRedoManager();
0414: return editorUndoManager;
0415: }
0416:
0417: void discardEditorUndoableEdits() {
0418: if (editorUndoManager != null)
0419: editorUndoManager.discardAllEdits();
0420: }
0421:
0422: // ------------
0423: // static getters
0424:
0425: JEditorPane getEditorPane() {
0426: return multiviewTC != null ? ((CloneableEditorSupport.Pane) multiviewTC)
0427: .getEditorPane()
0428: : null;
0429: }
0430:
0431: // -----------
0432: // closing/reloading
0433:
0434: public void reloadForm() {
0435: if (canClose())
0436: reloadDocument();
0437: }
0438:
0439: @Override
0440: protected org.openide.util.Task reloadDocument() {
0441: if (multiviewTC == null)
0442: return super .reloadDocument();
0443:
0444: org.openide.util.Task docLoadTask = super .reloadDocument();
0445:
0446: if (saving) // workaround for bug 75225
0447: return docLoadTask;
0448:
0449: // after reloading is done, open the form editor again
0450: java.awt.EventQueue.invokeLater(new Runnable() {
0451: public void run() {
0452: FormDesigner formDesigner = getFormEditor(true)
0453: .getFormDesigner();
0454: if (formDesigner == null) {
0455: formDesigner = (FormDesigner) multiviewTC
0456: .getClientProperty("formDesigner"); // NOI18N
0457: }
0458: if (formDesigner == null) {
0459: // if formDesigner is null then it haven't been activated yet...
0460: return;
0461: }
0462:
0463: // close
0464: getFormEditor().closeForm();
0465: formEditor = null;
0466:
0467: formDesigner.reset(getFormEditor(true));
0468: getFormEditor().setFormDesigner(formDesigner);
0469:
0470: if (formDesigner.isShowing()) {
0471: // load the form only if its open
0472: loadForm();
0473: FormEditor formEditor = getFormEditor();
0474: formEditor.reportErrors(FormEditor.LOADING);
0475: if (!formEditor.isFormLoaded()) { // there was a loading error
0476: formDesigner.removeAll();
0477: } else {
0478: formDesigner.initialize();
0479: }
0480: ComponentInspector.getInstance().focusForm(
0481: formEditor);
0482: formDesigner.revalidate();
0483: formDesigner.repaint();
0484: }
0485: }
0486: });
0487:
0488: return docLoadTask;
0489: }
0490:
0491: public FormEditor reloadFormEditor() {
0492: if (formEditor == null || !formEditor.isFormLoaded()) {
0493: return null;
0494: }
0495: FormDesigner formDesigner = formEditor.getFormDesigner();
0496: if (formDesigner == null) {
0497: formDesigner = (FormDesigner) multiviewTC
0498: .getClientProperty("formDesigner"); // NOI18N
0499: }
0500: formEditor.closeForm();
0501: formEditor = null;
0502: formEditor = getFormEditor(true); // new FormEditor instance
0503: if (formDesigner != null) {
0504: formDesigner.reset(formEditor);
0505: formEditor.setFormDesigner(formDesigner);
0506: }
0507: loadForm();
0508: formEditor.reportErrors(FormEditor.LOADING);
0509: if (formEditor.isFormLoaded() && formDesigner != null
0510: && formDesigner.isShowing()) {
0511: formDesigner.initialize();
0512: }
0513: return formEditor;
0514: }
0515:
0516: public void closeFormEditor() {
0517: if (isOpened()) {
0518: final FormDesigner formDesigner = formEditor
0519: .getFormDesigner();
0520: formEditor.closeForm();
0521: Runnable run = new Runnable() {
0522: public void run() {
0523: if (formDesigner != null) {
0524: formDesigner.reset(formEditor); // might be reused
0525: }
0526: selectJavaEditor();
0527: }
0528: };
0529: if (EventQueue.isDispatchThread()) {
0530: run.run();
0531: } else {
0532: try {
0533: java.awt.EventQueue.invokeAndWait(run);
0534: } catch (Exception ex) {
0535: ErrorManager.getDefault().notify(
0536: ErrorManager.INFORMATIONAL, ex);
0537: }
0538: }
0539: }
0540: }
0541:
0542: @Override
0543: protected void notifyClosed() {
0544: opened.remove(this );
0545: if (opened.isEmpty()) {
0546: detachTopComponentsListener();
0547: detachStatusListeners();
0548: }
0549:
0550: super .notifyClosed(); // close java editor
0551: if (formEditor != null) {
0552: formEditor.closeForm();
0553: formEditor = null;
0554: multiviewTC = null;
0555: }
0556: elementToOpen = JAVA_ELEMENT_INDEX;
0557: }
0558:
0559: private void multiViewClosed(CloneableTopComponent mvtc) {
0560: Enumeration en = mvtc.getReference().getComponents();
0561: boolean isLast = !en.hasMoreElements();
0562: if (multiviewTC == mvtc) {
0563: multiviewTC = null;
0564: FormDesigner formDesigner = null;
0565: // Find another multiviewTC, possibly with loaded formDesigner
0566: while (en.hasMoreElements()) {
0567: multiviewTC = (CloneableTopComponent) en.nextElement();
0568: FormDesigner designer = (FormDesigner) multiviewTC
0569: .getClientProperty("formDesigner"); // NOI18N
0570: if (designer != null) {
0571: formDesigner = designer;
0572: break;
0573: }
0574: }
0575: if (!isLast && (formDesigner == null)) {
0576: // Only Java elements are opened in the remaining clones
0577: if (formEditor != null) {
0578: formEditor.closeForm();
0579: formEditor = null;
0580: }
0581: }
0582: }
0583:
0584: if (isLast) // last view of this form closed
0585: notifyClosed();
0586: }
0587:
0588: @Override
0589: protected boolean notifyModified() {
0590: boolean alreadyModified = isModified();
0591: boolean retVal = super .notifyModified();
0592:
0593: if (retVal) { // java source modification
0594: addSaveCookie();
0595: }
0596:
0597: if (!alreadyModified) {
0598: FileObject formFile = formDataObject.getFormFile();
0599: if (!formFile.canWrite()) { // Issue 74092
0600: FileLock lock = null;
0601: try {
0602: lock = formFile.lock();
0603: } catch (UserQuestionException uqex) {
0604: NotifyDescriptor nd = new NotifyDescriptor.Confirmation(
0605: uqex.getLocalizedMessage(),
0606: FormUtils
0607: .getBundleString("TITLE_UserQuestion"), // NOI18N
0608: NotifyDescriptor.YES_NO_OPTION);
0609: DialogDisplayer.getDefault().notify(nd);
0610: if (NotifyDescriptor.YES_OPTION.equals(nd
0611: .getValue())) {
0612: try {
0613: uqex.confirmed();
0614: EventQueue.invokeLater(new Runnable() {
0615: public void run() {
0616: reloadForm();
0617: }
0618: });
0619: } catch (IOException ioex) {
0620: ErrorManager.getDefault().notify(
0621: ErrorManager.INFORMATIONAL, ioex);
0622: }
0623: }
0624: } catch (IOException ex) {
0625: ErrorManager.getDefault().notify(
0626: ErrorManager.INFORMATIONAL, ex);
0627: } finally {
0628: if (lock != null) {
0629: lock.releaseLock();
0630: }
0631: }
0632: }
0633: updateMVTCDisplayName();
0634: }
0635: return retVal;
0636: }
0637:
0638: @Override
0639: protected void notifyUnmodified() {
0640: super .notifyUnmodified();
0641: // java source modification
0642: removeSaveCookie();
0643: updateMVTCDisplayName();
0644: }
0645:
0646: private static void attachTopComponentsListener() {
0647: if (topcompsListener != null)
0648: return;
0649:
0650: topcompsListener = new PropertyChangeListener() {
0651: public void propertyChange(PropertyChangeEvent ev) {
0652: if (TopComponent.Registry.PROP_ACTIVATED.equals(ev
0653: .getPropertyName())) { // activated TopComponent has changed
0654: TopComponent active = TopComponent.getRegistry()
0655: .getActivated();
0656: if (getSelectedElementType(active) != -1) { // it is our multiview
0657: FormEditorSupport fes = getFormEditor(active);
0658: if (fes != null) {
0659: fes.multiviewTC = (CloneableTopComponent) active;
0660: FormDesigner designer = (FormDesigner) active
0661: .getClientProperty("formDesigner"); // NOI18N
0662: if (designer != null)
0663: fes.getFormEditor().setFormDesigner(
0664: designer);
0665: }
0666: }
0667: checkFormGroupVisibility();
0668: } else if (TopComponent.Registry.PROP_OPENED.equals(ev
0669: .getPropertyName())) { // set of opened TopComponents has changed - hasn't some
0670: // of our views been closed?
0671: CloneableTopComponent closedTC = null;
0672: Set oldSet = (Set) ev.getOldValue();
0673: Set newSet = (Set) ev.getNewValue();
0674: if (newSet.size() < oldSet.size()) {
0675: Iterator it = oldSet.iterator();
0676: while (it.hasNext()) {
0677: Object o = it.next();
0678: if (!newSet.contains(o)) {
0679: if (o instanceof CloneableTopComponent)
0680: closedTC = (CloneableTopComponent) o;
0681: break;
0682: }
0683: }
0684: }
0685: if (getSelectedElementType(closedTC) != -1) { // it is our multiview
0686: FormEditorSupport fes = getFormEditor(closedTC);
0687: if (fes != null)
0688: fes.multiViewClosed(closedTC);
0689: }
0690: TopComponent active = TopComponent.getRegistry()
0691: .getActivated();
0692: if (active != null
0693: && getSelectedElementType(active) != -1) { // it is our multiview
0694: FormEditorSupport fes = getFormEditor(active);
0695: if (fes != null) {
0696: fes.updateMVTCDisplayName();
0697: }
0698: }
0699: }
0700: }
0701: };
0702:
0703: TopComponent.getRegistry().addPropertyChangeListener(
0704: topcompsListener);
0705: }
0706:
0707: private static void detachTopComponentsListener() {
0708: if (topcompsListener != null) {
0709: TopComponent.getRegistry().removePropertyChangeListener(
0710: topcompsListener);
0711: topcompsListener = null;
0712:
0713: TopComponentGroup group = WindowManager.getDefault()
0714: .findTopComponentGroup("form"); // NOI18N
0715: if (group != null)
0716: group.close();
0717: }
0718: }
0719:
0720: // -------
0721: // window system & multiview
0722:
0723: @Override
0724: protected CloneableEditorSupport.Pane createPane() {
0725: if (!formDataObject.isValid()) {
0726: return super .createPane(); // Issue 110249
0727: }
0728: MultiViewDescription[] descs = new MultiViewDescription[] {
0729: new JavaDesc(formDataObject),
0730: new FormDesc(formDataObject) };
0731:
0732: CloneableTopComponent mvtc = MultiViewFactory
0733: .createCloneableMultiView(descs, descs[elementToOpen],
0734: new CloseHandler(formDataObject));
0735:
0736: // #45665 - dock into editor mode if possible..
0737: Mode editorMode = WindowManager.getDefault().findMode(
0738: CloneableEditorSupport.EDITOR_MODE);
0739: if (editorMode != null) {
0740: editorMode.dockInto(mvtc);
0741: }
0742: try {
0743: addStatusListener(formDataObject.getPrimaryFile()
0744: .getFileSystem());
0745: } catch (FileStateInvalidException fsiex) {
0746: fsiex.printStackTrace();
0747: }
0748: return (CloneableEditorSupport.Pane) mvtc;
0749: }
0750:
0751: private static String getMVTCToolTipText(
0752: FormDataObject formDataObject) {
0753: String name = FileUtil.getFileDisplayName(formDataObject
0754: .getFormFile());
0755: if (name.endsWith(".form")) { // NOI18N
0756: name = name.substring(0, name.length() - 5);
0757: }
0758: return name;
0759: }
0760:
0761: /**
0762: * Returns display name of the multiview top component.
0763: * The first item of the array is normal display name,
0764: * the second item of the array is HTML display name.
0765: *
0766: * @param formDataObject form data object representing the multiview tc.
0767: * @return display names of the MVTC. The second item can be <code>null</code>.
0768: */
0769: private static String[] getMVTCDisplayName(
0770: FormDataObject formDataObject) {
0771:
0772: boolean readonly = !formDataObject.getPrimaryFile().canWrite();
0773:
0774: TopComponent active = TopComponent.getRegistry().getActivated();
0775: FormEditorSupport fes = formDataObject.getFormEditor();
0776: FormModel fm = null;
0777: if (fes != null) {
0778: fm = fes.getFormModel();
0779: }
0780: if (active != null
0781: && getSelectedElementType(active) == FORM_ELEMENT_INDEX) {
0782: if (fm != null) {
0783: readonly = readonly || fm.isReadOnly();
0784: }
0785: }
0786:
0787: int version;
0788: if (formDataObject.isModified()) {
0789: version = readonly ? 2 : 1;
0790: } else {
0791: version = readonly ? 0 : 3;
0792: }
0793:
0794: Node node = formDataObject.getNodeDelegate();
0795: String htmlTitle = node.getHtmlDisplayName();
0796: String title = node.getDisplayName();
0797: if (fm != null) {
0798: FormDesigner fd = FormEditor.getFormDesigner(formDataObject
0799: .getFormEditor().getFormModel());
0800: if (fd != null && fd.getFormModel() != null) {
0801: if (fd.isShowing() && !fd.isTopRADComponent()
0802: && fd.getTopDesignComponent() != null) {
0803: title = FormUtils.getFormattedBundleString(
0804: "FMT_FormTitleWithContainerName", // NOI18N
0805: new Object[] {
0806: title,
0807: fd.getTopDesignComponent()
0808: .getName() });
0809: }
0810: }
0811: }
0812: if (htmlTitle != null) {
0813: if (!htmlTitle.trim().startsWith("<html>")) { // NOI18N
0814: htmlTitle = "<html>" + htmlTitle; // NOI18N
0815: }
0816: }
0817: return new String[] {
0818: FormUtils.getFormattedBundleString("FMT_FormMVTCTitle",
0819: new Object[] { new Integer(version), title }), // NOI18N
0820: (htmlTitle == null) ? null : FormUtils
0821: .getFormattedBundleString("FMT_FormMVTCTitle",
0822: new Object[] { new Integer(version),
0823: htmlTitle }) // NOI18N
0824: };
0825: }
0826:
0827: /** Updates title (display name) of all multiviews for given form. Replans
0828: * to event queue thread if necessary. */
0829: void updateMVTCDisplayName() {
0830: if (java.awt.EventQueue.isDispatchThread()) {
0831: updateMVTCDisplayNameInAWT();
0832: } else {
0833: java.awt.EventQueue.invokeLater(new Runnable() {
0834: public void run() {
0835: updateMVTCDisplayNameInAWT();
0836: }
0837: });
0838: }
0839: }
0840:
0841: private void updateMVTCDisplayNameInAWT() {
0842: if ((multiviewTC == null) || (!formDataObject.isValid())) // Issue 67544
0843: return;
0844:
0845: String[] titles = getMVTCDisplayName(formDataObject);
0846: Enumeration en = multiviewTC.getReference().getComponents();
0847: while (en.hasMoreElements()) {
0848: TopComponent tc = (TopComponent) en.nextElement();
0849: tc.setDisplayName(titles[0]);
0850: tc.setHtmlDisplayName(titles[1]);
0851: }
0852: }
0853:
0854: /** Updates tooltip of all multiviews for given form. Replans to even queue
0855: * thread if necessary. */
0856: void updateMVTCToolTipText() {
0857: if (java.awt.EventQueue.isDispatchThread()) {
0858: if (multiviewTC == null)
0859: return;
0860:
0861: String tooltip = getMVTCToolTipText(formDataObject);
0862: Enumeration en = multiviewTC.getReference().getComponents();
0863: while (en.hasMoreElements()) {
0864: TopComponent tc = (TopComponent) en.nextElement();
0865: tc.setToolTipText(tooltip);
0866: }
0867: } else {
0868: java.awt.EventQueue.invokeLater(new Runnable() {
0869: public void run() {
0870: if (multiviewTC == null)
0871: return;
0872:
0873: String tooltip = getMVTCToolTipText(formDataObject);
0874: Enumeration en = multiviewTC.getReference()
0875: .getComponents();
0876: while (en.hasMoreElements()) {
0877: TopComponent tc = (TopComponent) en
0878: .nextElement();
0879: tc.setToolTipText(tooltip);
0880: }
0881: }
0882: });
0883: }
0884: }
0885:
0886: static boolean isLastView(TopComponent tc) {
0887: if (!(tc instanceof CloneableTopComponent))
0888: return false;
0889:
0890: boolean oneOrLess = true;
0891: Enumeration en = ((CloneableTopComponent) tc).getReference()
0892: .getComponents();
0893: if (en.hasMoreElements()) {
0894: en.nextElement();
0895: if (en.hasMoreElements())
0896: oneOrLess = false;
0897: }
0898: return oneOrLess;
0899: }
0900:
0901: /** This is called by the multiview elements whenever they are created
0902: * (and given a observer knowing their multiview TopComponent). It is
0903: * important during deserialization and clonig the multiview - i.e. during
0904: * the operations we have no control over. But anytime a multiview is
0905: * created, this method gets called.
0906: */
0907: void setTopComponent(TopComponent topComp) {
0908: multiviewTC = (CloneableTopComponent) topComp;
0909: String[] titles = getMVTCDisplayName(formDataObject);
0910: multiviewTC.setDisplayName(titles[0]);
0911: multiviewTC.setHtmlDisplayName(titles[1]);
0912: multiviewTC.setToolTipText(getMVTCToolTipText(formDataObject));
0913: opened.add(this );
0914: attachTopComponentsListener();
0915: }
0916:
0917: public static FormEditorSupport getFormEditor(TopComponent tc) {
0918: Object dobj = tc.getLookup().lookup(DataObject.class);
0919: return dobj instanceof FormDataObject ? ((FormDataObject) dobj)
0920: .getFormEditorSupport() : null;
0921: }
0922:
0923: private static Boolean groupVisible = null;
0924:
0925: static void checkFormGroupVisibility() {
0926: // when active TopComponent changes, check if we should open or close
0927: // the form editor group of windows (Inspector, Palette, Properties)
0928: WindowManager wm = WindowManager.getDefault();
0929: final TopComponentGroup group = wm
0930: .findTopComponentGroup("form"); // NOI18N
0931: if (group == null)
0932: return; // group not found (should not happen)
0933:
0934: boolean designerSelected = false;
0935: Iterator it = wm.getModes().iterator();
0936: while (it.hasNext()) {
0937: Mode mode = (Mode) it.next();
0938: TopComponent selected = mode.getSelectedTopComponent();
0939: if (getSelectedElementType(selected) == FORM_ELEMENT_INDEX) {
0940: designerSelected = true;
0941: break;
0942: }
0943: }
0944:
0945: if (designerSelected && !Boolean.TRUE.equals(groupVisible)) {
0946: // Bug 116008: calling group.open() first time may cause hiding the
0947: // FormDesigner (some winsys multiview initialization mess), calling
0948: // this method again and hiding the group. By setting the groupVisible
0949: // to false we make the re-entrant call effectively do nothing.
0950: groupVisible = Boolean.FALSE;
0951: group.open();
0952: groupVisible = Boolean.TRUE;
0953: final TopComponentGroup paletteGroup = wm
0954: .findTopComponentGroup("commonpalette"); // NOI18N
0955: if (null != paletteGroup) {
0956: paletteGroup.open();
0957: }
0958: ComponentInspector inspector = ComponentInspector
0959: .getInstance();
0960: if (!Boolean.TRUE.equals(inspector
0961: .getClientProperty("isSliding"))) { // NOI18N
0962: inspector.requestVisible();
0963: }
0964: } else if (!designerSelected
0965: && !Boolean.FALSE.equals(groupVisible)) {
0966: group.close();
0967: groupVisible = Boolean.FALSE;
0968: }
0969: }
0970:
0971: /** @return 0 if java editor in form editor multiview is selected
0972: * 1 if form designer in form editor multiview is selected
0973: * -1 if the given TopComponent is not form editor multiview
0974: */
0975: static int getSelectedElementType(TopComponent tc) {
0976: if (tc != null) {
0977: MultiViewHandler handler = MultiViews
0978: .findMultiViewHandler(tc);
0979: if (handler != null) {
0980: String prefId = handler.getSelectedPerspective()
0981: .preferredID();
0982: if (MV_JAVA_ID.equals(prefId))
0983: return JAVA_ELEMENT_INDEX; // 0
0984: if (MV_FORM_ID.equals(prefId))
0985: return FORM_ELEMENT_INDEX; // 1
0986: }
0987: }
0988: return -1;
0989: }
0990:
0991: public SimpleSection getVariablesSection() {
0992: return getGuardedSectionManager().findSimpleSection(
0993: SECTION_VARIABLES);
0994: }
0995:
0996: public SimpleSection getInitComponentSection() {
0997: return getGuardedSectionManager().findSimpleSection(
0998: SECTION_INIT_COMPONENTS);
0999: }
1000:
1001: public GuardedSectionManager getGuardedSectionManager() {
1002: try {
1003: StyledDocument doc = openDocument();
1004: return GuardedSectionManager.getInstance(doc);
1005: } catch (IOException ex) {
1006: throw (IllegalStateException) new IllegalStateException(
1007: "cannot open document").initCause(ex); // NOI18N
1008: }
1009: }
1010:
1011: private final class FormGEditor implements GuardedEditorSupport {
1012:
1013: StyledDocument doc = null;
1014:
1015: public StyledDocument getDocument() {
1016: return FormGEditor.this .doc;
1017: }
1018: }
1019:
1020: private FormGEditor guardedEditor;
1021: private GuardedSectionsProvider guardedProvider;
1022:
1023: @Override
1024: protected void loadFromStreamToKit(StyledDocument doc,
1025: InputStream stream, EditorKit kit) throws IOException,
1026: BadLocationException {
1027: if (guardedEditor == null) {
1028: guardedEditor = new FormGEditor();
1029: GuardedSectionsFactory gFactory = GuardedSectionsFactory
1030: .find(((DataEditorSupport.Env) env).getMimeType());
1031: if (gFactory != null) {
1032: guardedProvider = gFactory.create(guardedEditor);
1033: }
1034: }
1035:
1036: if (guardedProvider != null) {
1037: guardedEditor.doc = doc;
1038: Charset c = FileEncodingQuery.getEncoding(this
1039: .getDataObject().getPrimaryFile());
1040: Reader reader = guardedProvider.createGuardedReader(stream,
1041: c);
1042: try {
1043: kit.read(reader, doc, 0);
1044: } finally {
1045: reader.close();
1046: }
1047: } else {
1048: super .loadFromStreamToKit(doc, stream, kit);
1049: }
1050: }
1051:
1052: @Override
1053: protected void saveFromKitToStream(StyledDocument doc,
1054: EditorKit kit, OutputStream stream) throws IOException,
1055: BadLocationException {
1056: if (guardedProvider != null) {
1057: Charset c = FileEncodingQuery.getEncoding(this
1058: .getDataObject().getPrimaryFile());
1059: Writer writer = guardedProvider.createGuardedWriter(stream,
1060: c);
1061: try {
1062: kit.write(writer, doc, 0, doc.getLength());
1063: } finally {
1064: writer.close();
1065: }
1066: } else {
1067: super .saveFromKitToStream(doc, kit, stream);
1068: }
1069: }
1070:
1071: // --------
1072:
1073: /** A descriptor for the FormDesigner element of multiview. Allows lazy
1074: * creation of the FormDesigner (and thus form loading). */
1075: private static class FormDesc implements MultiViewDescription,
1076: Serializable {
1077:
1078: private static final long serialVersionUID = -3126744316624172415L;
1079:
1080: private DataObject dataObject;
1081:
1082: private FormDesc() {
1083: }
1084:
1085: public FormDesc(DataObject formDO) {
1086: dataObject = formDO;
1087: }
1088:
1089: private FormEditorSupport getFormEditor() {
1090: return dataObject != null
1091: && dataObject instanceof FormDataObject ? ((FormDataObject) dataObject)
1092: .getFormEditorSupport()
1093: : null;
1094: }
1095:
1096: public MultiViewElement createElement() {
1097: FormEditorSupport formEditor = getFormEditor();
1098: return new FormDesigner((formEditor == null) ? null
1099: : formEditor.getFormEditor(true));
1100: }
1101:
1102: public String getDisplayName() {
1103: return FormUtils.getBundleString("CTL_DesignTabCaption"); // NOI18N
1104: }
1105:
1106: public org.openide.util.HelpCtx getHelpCtx() {
1107: return org.openide.util.HelpCtx.DEFAULT_HELP;
1108: }
1109:
1110: public java.awt.Image getIcon() {
1111: return Utilities.loadImage(iconURL);
1112: }
1113:
1114: public int getPersistenceType() {
1115: return TopComponent.PERSISTENCE_NEVER;
1116: }
1117:
1118: public String preferredID() {
1119: return MV_FORM_ID;
1120: }
1121:
1122: public void writeExternal(ObjectOutput out) throws IOException {
1123: out.writeObject(dataObject);
1124: }
1125:
1126: public void readExternal(ObjectInput in) throws IOException,
1127: ClassNotFoundException {
1128: Object firstObject = in.readObject();
1129: if (firstObject instanceof FormDataObject)
1130: dataObject = (DataObject) firstObject;
1131: }
1132: }
1133:
1134: // -------
1135:
1136: /** A descriptor for the java editor as an element in multiview. */
1137: private static class JavaDesc implements MultiViewDescription,
1138: SourceViewMarker, Serializable {
1139:
1140: private static final long serialVersionUID = -3126744316624172415L;
1141:
1142: private DataObject dataObject;
1143:
1144: private JavaDesc() {
1145: }
1146:
1147: public JavaDesc(DataObject formDO) {
1148: dataObject = formDO;
1149: }
1150:
1151: private FormEditorSupport getJavaEditor() {
1152: return dataObject != null
1153: && dataObject instanceof FormDataObject ? ((FormDataObject) dataObject)
1154: .getFormEditorSupport()
1155: : null;
1156: }
1157:
1158: public MultiViewElement createElement() {
1159: FormEditorSupport javaEditor = getJavaEditor();
1160: if (javaEditor != null) {
1161: javaEditor.prepareDocument();
1162: JavaEditorTopComponent editor = new JavaEditorTopComponent(
1163: dataObject.getCookie(FormEditorSupport.class));
1164: Node[] nodes = editor.getActivatedNodes();
1165: if ((nodes == null) || (nodes.length == 0)) {
1166: editor.setActivatedNodes(new Node[] { dataObject
1167: .getNodeDelegate() });
1168: }
1169: return (MultiViewElement) editor;
1170: }
1171: return MultiViewFactory.BLANK_ELEMENT;
1172: }
1173:
1174: public String getDisplayName() {
1175: return FormUtils.getBundleString("CTL_SourceTabCaption"); // NOI18N
1176: }
1177:
1178: public org.openide.util.HelpCtx getHelpCtx() {
1179: return org.openide.util.HelpCtx.DEFAULT_HELP;
1180: }
1181:
1182: public java.awt.Image getIcon() {
1183: return Utilities.loadImage(iconURL);
1184: }
1185:
1186: public int getPersistenceType() {
1187: return TopComponent.PERSISTENCE_ONLY_OPENED;
1188: }
1189:
1190: public String preferredID() {
1191: return MV_JAVA_ID;
1192: }
1193:
1194: public void writeExternal(ObjectOutput out) throws IOException {
1195: out.writeObject(dataObject);
1196: }
1197:
1198: public void readExternal(ObjectInput in) throws IOException,
1199: ClassNotFoundException {
1200: Object firstObject = in.readObject();
1201: if (firstObject instanceof FormDataObject)
1202: dataObject = (DataObject) firstObject;
1203: }
1204: }
1205:
1206: // --------
1207:
1208: private static class JavaEditorTopComponent extends CloneableEditor
1209: implements MultiViewElement {
1210: private static final long serialVersionUID = -3126744316624172415L;
1211:
1212: private transient JComponent toolbar;
1213:
1214: private transient MultiViewElementCallback multiViewObserver;
1215:
1216: JavaEditorTopComponent() {
1217: super ();
1218: }
1219:
1220: JavaEditorTopComponent(DataEditorSupport s) {
1221: super (s);
1222: }
1223:
1224: public JComponent getToolbarRepresentation() {
1225: if (toolbar == null) {
1226: JEditorPane pane = getEditorPane();
1227: if (pane != null) {
1228: Document doc = pane.getDocument();
1229: if (doc instanceof NbDocument.CustomToolbar) {
1230: toolbar = ((NbDocument.CustomToolbar) doc)
1231: .createToolbar(pane);
1232: }
1233: }
1234: if (toolbar == null) {
1235: // attempt to create own toolbar??
1236: toolbar = new JPanel();
1237: }
1238: }
1239: return toolbar;
1240: }
1241:
1242: public JComponent getVisualRepresentation() {
1243: return this ;
1244: }
1245:
1246: @Override
1247: public void componentDeactivated() {
1248: super .componentDeactivated();
1249: }
1250:
1251: @Override
1252: public void componentActivated() {
1253: super .componentActivated();
1254: }
1255:
1256: public void setMultiViewCallback(
1257: MultiViewElementCallback callback) {
1258: multiViewObserver = callback;
1259:
1260: // needed for deserialization...
1261: if (((DataEditorSupport) cloneableEditorSupport())
1262: .getDataObject() instanceof FormDataObject) { // [obj is from EditorSupport.Editor]
1263: // this is used (or misused?) to obtain the deserialized
1264: // multiview topcomponent and set it to FormEditorSupport
1265: ((FormDataObject) ((DataEditorSupport) cloneableEditorSupport())
1266: .getDataObject()).getFormEditorSupport()
1267: .setTopComponent(callback.getTopComponent());
1268: }
1269: }
1270:
1271: @Override
1272: public void requestVisible() {
1273: if (multiViewObserver != null)
1274: multiViewObserver.requestVisible();
1275: else
1276: super .requestVisible();
1277: }
1278:
1279: @Override
1280: public void requestActive() {
1281: if (multiViewObserver != null)
1282: multiViewObserver.requestActive();
1283: else
1284: super .requestActive();
1285: }
1286:
1287: @Override
1288: public void componentClosed() {
1289: // Issue 52286 & 55818
1290: super .canClose(null, true);
1291: super .componentClosed();
1292: }
1293:
1294: @Override
1295: public void componentShowing() {
1296: super .componentShowing();
1297: DataObject dob = ((DataEditorSupport) cloneableEditorSupport())
1298: .getDataObject();
1299: FormDataObject formDO = (FormDataObject) dob;
1300: FormModel model = null;
1301: if (formDO != null) {
1302: FormEditorSupport fe = formDO.getFormEditor();
1303: if (fe != null) {
1304: model = fe.getFormModel();
1305: if (model != null) {
1306: JavaCodeGenerator codeGen = (JavaCodeGenerator) FormEditor
1307: .getCodeGenerator(model);
1308: codeGen.regenerateCode();
1309: }
1310: }
1311: }
1312:
1313: }
1314:
1315: @Override
1316: public void componentHidden() {
1317: super .componentHidden();
1318: }
1319:
1320: @Override
1321: public void componentOpened() {
1322: super .componentOpened();
1323: DataObject dob = ((DataEditorSupport) cloneableEditorSupport())
1324: .getDataObject();
1325: if ((multiViewObserver != null)
1326: && !(dob instanceof FormDataObject)) {
1327: multiViewObserver.getTopComponent().close(); // Issue 67879
1328: EditorCookie ec = dob.getCookie(EditorCookie.class);
1329: ec.open();
1330: }
1331: }
1332:
1333: @Override
1334: public void updateName() {
1335: super .updateName();
1336: if (multiViewObserver != null) {
1337: FormDataObject formDataObject = (FormDataObject) ((DataEditorSupport) cloneableEditorSupport())
1338: .getDataObject();
1339: String[] titles = getMVTCDisplayName(formDataObject);
1340: setDisplayName(titles[0]);
1341: setHtmlDisplayName(titles[1]);
1342: }
1343: }
1344:
1345: @Override
1346: protected boolean closeLast() {
1347: return true;
1348: }
1349:
1350: public CloseOperationState canCloseElement() {
1351: // if this is not the last cloned java editor component, closing is OK
1352: if (!FormEditorSupport.isLastView(multiViewObserver
1353: .getTopComponent()))
1354: return CloseOperationState.STATE_OK;
1355:
1356: // return a placeholder state - to be sure our CloseHandler is called
1357: return MultiViewFactory.createUnsafeCloseState(
1358: "ID_JAVA_CLOSING", // dummy ID // NOI18N
1359: MultiViewFactory.NOOP_CLOSE_ACTION,
1360: MultiViewFactory.NOOP_CLOSE_ACTION);
1361: }
1362:
1363: protected boolean isActiveTC() {
1364: TopComponent selected = getRegistry().getActivated();
1365:
1366: if (selected == null)
1367: return false;
1368: if (selected == this )
1369: return true;
1370:
1371: MultiViewHandler handler = MultiViews
1372: .findMultiViewHandler(selected);
1373: if (handler != null
1374: && MV_JAVA_ID.equals(handler
1375: .getSelectedPerspective().preferredID()))
1376: return true;
1377:
1378: return false;
1379: }
1380: }
1381:
1382: // ------
1383:
1384: /** Implementation of CloseOperationHandler for multiview. Ensures both form
1385: * and java editor are correctly closed, data saved, etc. Holds a reference
1386: * to form DataObject only - to be serializable with the multiview
1387: * TopComponent without problems.
1388: */
1389: private static class CloseHandler implements CloseOperationHandler,
1390: Serializable {
1391: private static final long serialVersionUID = -3126744315424172415L;
1392:
1393: private DataObject dataObject;
1394:
1395: private CloseHandler() {
1396: }
1397:
1398: public CloseHandler(DataObject formDO) {
1399: dataObject = formDO;
1400: }
1401:
1402: private FormEditorSupport getFormEditor() {
1403: return dataObject != null
1404: && dataObject instanceof FormDataObject ? ((FormDataObject) dataObject)
1405: .getFormEditorSupport()
1406: : null;
1407: }
1408:
1409: public boolean resolveCloseOperation(
1410: CloseOperationState[] elements) {
1411: FormEditorSupport formEditor = getFormEditor();
1412: return formEditor != null ? formEditor.canClose() : true;
1413: }
1414: }
1415:
1416: // ------
1417:
1418: private static final class Environment extends
1419: DataEditorSupport.Env {
1420:
1421: private static final long serialVersionUID = -1;
1422:
1423: public Environment(DataObject obj) {
1424: super (obj);
1425: }
1426:
1427: @Override
1428: protected FileObject getFile() {
1429: return this .getDataObject().getPrimaryFile();
1430: }
1431:
1432: @Override
1433: protected FileLock takeLock() throws java.io.IOException {
1434: return ((FormDataObject) getDataObject()).getPrimaryEntry()
1435: .takeLock();
1436: }
1437:
1438: @Override
1439: public CloneableOpenSupport findCloneableOpenSupport() {
1440: return this .getDataObject().getCookie(
1441: FormEditorSupport.class);
1442: }
1443:
1444: }
1445:
1446: private final SaveCookie saveCookie = new SaveCookie() {
1447: public void save() throws java.io.IOException {
1448: if (formEditor == null) { // not saving form, only java
1449: doSave(false); // don't need to be in event dispatch thread (#102986)
1450: } else if (EventQueue.isDispatchThread()) {
1451: doSave(true);
1452: } else {
1453: try {
1454: EventQueue.invokeAndWait(new Runnable() {
1455: public void run() {
1456: doSave(true);
1457: }
1458: });
1459: } catch (InterruptedException ex) {
1460: Logger.getLogger(FormEditorSupport.class.getName())
1461: .log(Level.INFO, "", ex); // NOI18N
1462: } catch (InvocationTargetException ex) {
1463: Logger.getLogger(FormEditorSupport.class.getName())
1464: .log(Level.INFO, "", ex); // NOI18N
1465: }
1466: }
1467: }
1468:
1469: private void doSave(boolean bothJavaAndForm) {
1470: try {
1471: if (bothJavaAndForm) {
1472: saveDocument();
1473: } else {
1474: saveSourceOnly();
1475: }
1476: } catch (IOException ex) {
1477: Logger.getLogger(FormEditorSupport.class.getName())
1478: .log(Level.INFO, "", ex); // NOI18N
1479: }
1480: }
1481: };
1482:
1483: private final CookieSet cookies;
1484:
1485: public void addSaveCookie() {
1486: DataObject javaData = this .getDataObject();
1487: if (javaData.getCookie(SaveCookie.class) == null) {
1488: cookies.add(saveCookie);
1489: javaData.setModified(true);
1490: }
1491: }
1492:
1493: public void removeSaveCookie() {
1494: DataObject javaData = this .getDataObject();
1495: if (javaData.getCookie(SaveCookie.class) != null) {
1496: cookies.remove(saveCookie);
1497: javaData.setModified(false);
1498: }
1499: }
1500: }
|