blob: b2678c19d4241f62d20b41d0f105acb03562d2c0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.nbform;
import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.SourcePositions;
import java.awt.Cursor;
import java.awt.Dialog;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.beans.BeanInfo;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.CharConversionException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.text.BadLocationException;
import javax.swing.text.EditorKit;
import javax.swing.text.Position;
import javax.swing.text.StyledDocument;
import org.netbeans.api.editor.fold.Fold;
import org.netbeans.api.editor.fold.FoldHierarchy;
import org.netbeans.api.editor.fold.FoldHierarchyEvent;
import org.netbeans.api.editor.fold.FoldUtilities;
import org.netbeans.api.editor.guards.GuardedSectionManager;
import org.netbeans.api.editor.guards.SimpleSection;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.queries.SourceLevelQuery;
import org.netbeans.api.java.source.CancellableTask;
import org.netbeans.api.java.source.CodeStyle;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.TreeUtilities;
import org.netbeans.api.java.source.WorkingCopy;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.libraries.Library;
import org.netbeans.api.project.libraries.LibraryManager;
import org.netbeans.api.queries.FileEncodingQuery;
import org.netbeans.core.api.multiview.MultiViewHandler;
import org.netbeans.core.api.multiview.MultiViews;
import org.netbeans.core.spi.multiview.CloseOperationState;
import org.netbeans.core.spi.multiview.MultiViewElement;
import org.netbeans.core.spi.multiview.MultiViewElementCallback;
import org.netbeans.core.spi.multiview.MultiViewFactory;
import org.netbeans.core.spi.multiview.text.MultiViewEditorElement;
import org.netbeans.modules.form.CodeGenerator;
import org.netbeans.modules.form.EditorSupport;
import org.netbeans.modules.form.FormDataObject;
import org.netbeans.modules.form.FormDesigner;
import org.netbeans.modules.form.FormEditor;
import org.netbeans.modules.form.FormModel;
import org.netbeans.modules.form.FormUtils;
import org.netbeans.modules.form.PersistenceException;
import org.netbeans.modules.form.project.ClassPathUtils;
import org.netbeans.modules.form.project.ClassSource;
import org.netbeans.modules.nbform.project.ClassSourceResolver;
import org.netbeans.spi.editor.guards.GuardedEditorSupport;
import org.netbeans.spi.editor.guards.GuardedSectionsFactory;
import org.netbeans.spi.editor.guards.GuardedSectionsProvider;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.NotifyDescriptor;
import org.openide.awt.Mnemonics;
import org.openide.awt.StatusDisplayer;
import org.openide.awt.UndoRedo;
import org.openide.cookies.CloseCookie;
import org.openide.cookies.EditorCookie;
import org.openide.cookies.OpenCookie;
import org.openide.cookies.PrintCookie;
import org.openide.cookies.SaveCookie;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.filesystems.FileStatusEvent;
import org.openide.filesystems.FileStatusListener;
import org.openide.filesystems.FileSystem;
import org.openide.loaders.DataObject;
import org.openide.nodes.CookieSet;
import org.openide.nodes.Node;
import org.openide.nodes.NodeAdapter;
import org.openide.nodes.NodeListener;
import org.openide.text.CloneableEditorSupport;
import org.openide.text.DataEditorSupport;
import org.openide.text.PositionRef;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.NbBundle.Messages;
import org.openide.util.UserQuestionException;
import org.openide.windows.CloneableOpenSupport;
import org.openide.windows.CloneableTopComponent;
import org.openide.windows.Mode;
import org.openide.windows.TopComponent;
import org.openide.windows.TopComponentGroup;
import org.openide.windows.WindowManager;
import org.openide.xml.XMLUtil;
/**
*
* @author Ian Formanek, Tomas Pavek
*/
public final class FormEditorSupport extends DataEditorSupport implements EditorSupport,
EditorCookie.Observable, CloseCookie, PrintCookie {
/** ID of the form designer (in the multiview) */
static final String MV_FORM_ID = "form"; //NOI18N
/** ID of the java editor (in the multiview) */
private static final String MV_JAVA_ID = "java"; // NOI18N
private static final int JAVA_ELEMENT_INDEX = 0;
private static final int FORM_ELEMENT_INDEX = 2;
private int elementToOpen; // default element index when multiview TC is created
/** Icon for the form editor multiview window */
static final String iconURL =
"org/netbeans/modules/form/resources/form.gif"; // NOI18N
/** The DataObject of the form */
private FormDataObject formDataObject;
/** The embracing multiview TopComponent (holds the form designer and
* java editor) - we remember the last active TopComponent (not all clones) */
private CloneableTopComponent multiviewTC;
/**
* Listener for node delegate's icon changes. It is responsible
* for synchronization of node's and multiviewTC's icons.
*/
private NodeListener nodeListener;
private static PropertyChangeListener topcompsListener;
private FoldHierarchyListener foldHierarchyListener;
private UndoRedo.Manager editorUndoManager;
private FormEditor formEditor;
/** Set of opened FormEditorSupport instances (java or form opened) */
private static final Set<FormEditorSupport> opened = Collections.synchronizedSet(new HashSet<FormEditorSupport>());
private static Map<FileSystem,FileStatusListener> fsToStatusListener = new HashMap<FileSystem,FileStatusListener>();
// --------------
// constructor
public FormEditorSupport(FormDataObject formDataObject) {
super(formDataObject, formDataObject.getLookup(), new Environment(formDataObject));
setMIMEType("text/x-java"); // NOI18N
this.formDataObject = formDataObject;
this.cookies = formDataObject.getCookies();
}
// ----------
// opening & saving interface methods
/** Main entry method. Called by OpenCookie implementation - opens the form.
*
* @param forceFormElement determines whether we should force switch to form element.
* @see OpenCookie#open
*/
public void openFormEditor(boolean forceFormElement) {
boolean alreadyOpened = opened.contains(this);
boolean switchToForm = forceFormElement || !alreadyOpened;
if (switchToForm) {
elementToOpen = FORM_ELEMENT_INDEX;
}
long ms = System.currentTimeMillis();
try {
showOpeningStatus("FMT_PreparingForm"); // NOI18N
multiviewTC = openCloneableTopComponent();
multiviewTC.requestActive();
registerNodeListener();
if (switchToForm) {
MultiViewHandler handler = MultiViews.findMultiViewHandler(multiviewTC);
handler.requestActive(handler.getPerspectives()[FORM_ELEMENT_INDEX]);
}
} finally {
hideOpeningStatus();
}
Logger.getLogger(FormEditor.class.getName()).log(Level.FINER, "Opening form time 1: {0}ms", (System.currentTimeMillis()-ms)); // NOI18N
}
private void registerNodeListener() {
if (formDataObject.isValid()) {
Node node = formDataObject.getNodeDelegate();
multiviewTC.setIcon(node.getIcon(BeanInfo.ICON_COLOR_16x16));
if (nodeListener == null) {
NodeListener listener = new NodeAdapter() {
@Override
public void propertyChange(final PropertyChangeEvent ev) {
Mutex.EVENT.writeAccess(new Runnable() {
@Override
public void run() {
if (Node.PROP_ICON.equals(ev.getPropertyName())) {
if (formDataObject.isValid() && (multiviewTC != null)) {
multiviewTC.setIcon(formDataObject.getNodeDelegate().getIcon(BeanInfo.ICON_COLOR_16x16));
}
}
}
});
}
};
node.addNodeListener(org.openide.nodes.NodeOp.weakNodeListener(listener, node));
nodeListener = listener;
}
}
}
void showOpeningStatus(String fmtMessage) {
JFrame mainWin = (JFrame) WindowManager.getDefault().getMainWindow();
// set wait cursor
mainWin.getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
mainWin.getGlassPane().setVisible(true);
// set status text like "Opening form: ..."
StatusDisplayer.getDefault().setStatusText(
FormUtils.getFormattedBundleString(
fmtMessage, // NOI18N
new Object[] { formDataObject.getFormFile().getName() }));
javax.swing.RepaintManager.currentManager(mainWin).paintDirtyRegions();
}
void hideOpeningStatus() {
// clear wait cursor
JFrame mainWin = (JFrame) WindowManager.getDefault().getMainWindow();
mainWin.getGlassPane().setVisible(false);
mainWin.getGlassPane().setCursor(null);
StatusDisplayer.getDefault().setStatusText(""); // NOI18N
}
private void addStatusListener(FileSystem fs) {
FileStatusListener fsl = fsToStatusListener.get(fs);
if (fsl == null) {
fsl = new FileStatusListener() {
@Override
public void annotationChanged(FileStatusEvent ev) {
synchronized (opened) {
Iterator<FormEditorSupport> iter = opened.iterator();
while (iter.hasNext()) {
FormEditorSupport fes = iter.next();
if (ev.hasChanged(fes.getFormDataObject().getPrimaryFile())
|| ev.hasChanged(fes.getFormDataObject().getFormFile())) {
fes.updateMVTCDisplayName();
}
}
}
}
};
fs.addFileStatusListener(fsl);
fsToStatusListener.put(fs, fsl);
} // else do nothing - the listener is already added
}
private static void detachStatusListeners() {
Iterator iter = fsToStatusListener.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next();
FileSystem fs = (FileSystem)entry.getKey();
FileStatusListener fsl = (FileStatusListener)entry.getValue();
fs.removeFileStatusListener(fsl);
}
fsToStatusListener.clear();
}
void selectJavaEditor(){
MultiViewHandler handler = MultiViews.findMultiViewHandler(multiviewTC);
if (handler != null) {
handler.requestActive(handler.getPerspectives()[JAVA_ELEMENT_INDEX]);
}
}
@Override
protected boolean asynchronousOpen() {
return false;
}
@Override
public void openSource() {
open();
}
@Override
public void openDesign() {
if (EventQueue.isDispatchThread()) {
openFormEditor(true);
} else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
openFormEditor(true);
}
});
}
}
/** Overriden from JavaEditor - opens editor and ensures it is selected
* in the multiview.
*/
@Override
public void open() {
if (EventQueue.isDispatchThread()) {
openInAWT();
} else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
openInAWT();
}
});
}
}
private void openInAWT() {
if (!formDataObject.isValid()) {
return;
}
if (Boolean.TRUE.equals(formDataObject.getPrimaryFile().getAttribute("nonEditableTemplate"))) { // NOI18N
String pattern = FormUtils.getBundleString("MSG_NonEditableTemplate"); // NOI18N
String message = MessageFormat.format(pattern, new Object[] {formDataObject.getNodeDelegate().getName()});
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(message));
return;
}
elementToOpen = JAVA_ELEMENT_INDEX;
super.open();
// This method must be executed in AWT thread because
// otherwise multiview is opened in AWT using invokeLater
// and we don't have multiviewTC correctly set
MultiViewHandler handler = MultiViews.findMultiViewHandler(multiviewTC);
if (handler != null) {
handler.requestActive(handler.getPerspectives()[JAVA_ELEMENT_INDEX]);
// will continue in loadOpeningForm
}
}
/** Overriden from JavaEditor - opens editor at given position and ensures
* it is selected in the multiview.
*
* @param pos position
*/
public void openAt(PositionRef pos) {
elementToOpen = JAVA_ELEMENT_INDEX;
openCloneableTopComponent();
MultiViewHandler handler = MultiViews.findMultiViewHandler(multiviewTC);
handler.requestActive(handler.getPerspectives()[JAVA_ELEMENT_INDEX]);
openAt(pos, -1).getComponent().requestActive();
}
@Override
public void openAt(Position pos) {
openAt(createPositionRef(pos.getOffset(), Position.Bias.Forward));
}
boolean startFormLoading() {
if (!formEditor.prepareLoading()) {
// report errors and switch to Source tab - later because now in the
// middle of switching to Design (error dialog would run event queue
// and process waiting tasks too soon, e.g. closing - bug 223487)
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
if (formEditor != null) { // not closed yet
reportErrors();
selectJavaEditor();
}
}
});
return false;
}
return true;
}
/**
* Callback from multiview (FormDesignerTC), requiring to load form whose
* designer is just being opened.
* @return true if successfully loaded
*/
boolean loadOpeningForm() {
showOpeningStatus("FMT_OpeningForm"); // NOI18N
postCreationUpdate1();
boolean success = formEditor.loadFormData();
postCreationUpdate2();
hideOpeningStatus();
reportErrors(); // report errors during loading, fatal or non-fatal
if (!success) { // loading failed - don't keep empty designer opened
selectJavaEditor();
}
return success;
}
/** Public method for loading form data from file. Does not open the
* source editor and designer, does not report errors and does not throw
* any exceptions. Runs in AWT event dispatch thread, returns after the
* form is loaded (even if not called from AWT thread).
* @return whether the form is loaded (true also if it already was)
*/
public boolean loadForm() {
return getFormEditor(true).loadForm();
}
/** @return true if the form is opened, false otherwise */
public boolean isOpened() {
return (formEditor != null) && formEditor.isFormLoaded();
}
private boolean saving; // workaround for bug 75225
/** Save the document in this thread and start reparsing it.
* @exception IOException on I/O error
*/
@Override
public void saveDocument() throws IOException {
IOException ioEx = null;
try {
if (formEditor != null) {
formEditor.saveFormData();
}
saving = true; // workaround for bug 75225
super.saveDocument();
}
catch (PersistenceException ex) {
Throwable t = ex.getOriginalException();
if (t instanceof IOException)
ioEx = (IOException) t;
else {
ioEx = new IOException("Cannot save the form"); // NOI18N
ErrorManager.getDefault().annotate(ioEx, t != null ? t : ex);
}
}
finally {
saving = false; // workaround for bug 75225
}
if (formEditor != null) {
formEditor.reportSavingErrors(); // TODO can't just throw IOException?
}
if (ioEx != null)
throw ioEx;
}
public void saveSourceOnly() throws IOException {
try {
saving = true; // workaround for bug 75225
super.saveDocument();
} finally {
saving = false; // workaround for bug 75225
}
}
/**
* Reports errors occurred during loading or saving the form.
*/
private void reportErrors() {
if (!formEditor.isFormLoaded()) {
formEditor.reportLoadingErrors(null); // fatal error, no options
} else {
// The form was loaded with some non-fatal errors - some data
// was not loaded - show a warning about possible data loss.
// The dialog is shown later to let the designer opening complete.
JButton viewButton = new JButton();
Mnemonics.setLocalizedText(viewButton, FormUtils.getBundleString("CTL_ViewOnly")); // NOI18N
JButton editButton = new JButton();
Mnemonics.setLocalizedText(editButton, FormUtils.getBundleString("CTL_AllowEditing")); // NOI18N
final Object[] options = new Object[] { viewButton, editButton,
NotifyDescriptor.CANCEL_OPTION };
final DialogDescriptor dd = formEditor.reportLoadingErrors(options);
if (dd != null) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
if (formEditor != null && !formEditor.isFormLoaded()) {
return; // quite unlikely, but the form could be closed meanwhile (#164444)
}
Dialog dialog = DialogDisplayer.getDefault().createDialog(dd);
// hack: adjust focus so it is not on the Show Exceptions button
if (dialog instanceof JDialog) {
((JDialog)dialog).getContentPane().requestFocus();
}
dialog.setVisible(true);
dialog.dispose();
Object ret = dd.getValue();
if (ret == options[0]) { // View Only
formEditor.setFormReadOnly();
updateMVTCDisplayName();
} else if (ret == options[1]) { // Allow Editing
formEditor.destroyInvalidComponents();
} else { // close form, switch to source editor
closeFormEditor();
}
}
});
}
}
}
// ------------
// other interface methods
/** @return data object representing the form */
public final FormDataObject getFormDataObject() {
return formDataObject;
}
public FormModel getFormModel() {
FormEditor fe = getFormEditor();
return (fe == null) ? null : fe.getFormModel();
}
public FormEditor getFormEditor() {
return getFormEditor(false);
}
FormEditor getFormEditor(boolean initialize) {
if ((formEditor == null) && initialize) {
formEditor = new FormEditor(formDataObject, this);
}
return formEditor;
}
private FormDesignerTC getFormDesignerTC() {
if (multiviewTC == null) {
return null;
}
return (FormDesignerTC) multiviewTC.getClientProperty("formDesigner"); // NOI18N
}
/** Marks the form as modified if it's not yet. Used if changes made
* in form data don't affect the java source file (generated code). */
@Override
public void markModified() {
if (formEditor != null && formEditor.isFormLoaded() && !formDataObject.isModified()) {
notifyModified();
}
}
@Override
protected UndoRedo.Manager createUndoRedoManager() {
editorUndoManager = super.createUndoRedoManager();
return editorUndoManager;
}
@Override
public void discardEditorUndoableEdits() {
if (editorUndoManager != null)
editorUndoManager.discardAllEdits();
}
// -----------
// closing/reloading
@Override
public void reloadForm() {
if (canClose())
reloadDocument();
}
@Override
protected org.openide.util.Task reloadDocument() {
if (multiviewTC == null)
return super.reloadDocument();
org.openide.util.Task docLoadTask = super.reloadDocument();
if (saving) // workaround for bug 75225
return docLoadTask;
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
FormDesignerTC designerTC = getFormDesignerTC();
if (designerTC != null) {
formEditor.closeForm();
designerTC.resetDesigner(true); // will trigger loading
}
}
});
return docLoadTask;
}
/**
* Reload the form if opened. It always loads the form, no matter whether on
* Source or Design tabs. That's different from standard reload task that
* won't load the form if currently not in Design.
*/
public FormEditor reloadFormEditor() {
if (!isOpened()) {
return null;
}
FormDesignerTC designerTC = getFormDesignerTC();
formEditor.closeForm();
boolean success = formEditor.loadForm();
if (designerTC != null) {
if (success) {
designerTC.resetDesigner(true);
} else {
closeFormEditor();
}
}
reportErrors();
return formEditor;
}
/**
* Closes the form editor without closing the document in editor. The editor
* is switched to Source tab, later clicking on Design tab will load the
* form again.
*/
public void closeFormEditor() {
if (isOpened()) {
final FormDesignerTC designerTC = getFormDesignerTC();
formEditor.closeForm();
Runnable run = new Runnable() {
@Override
public void run() {
if (designerTC != null) {
designerTC.resetDesigner(false);
}
selectJavaEditor();
}
};
if (EventQueue.isDispatchThread()) {
run.run();
} else {
try {
java.awt.EventQueue.invokeAndWait(run);
} catch (Exception ex) {
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
}
}
}
}
@Override
protected void notifyClosed() {
opened.remove(this);
if (opened.isEmpty()) {
detachTopComponentsListener();
detachStatusListeners();
}
super.notifyClosed(); // close java editor
if (formEditor != null) {
formEditor.closeForm();
formEditor = null;
}
nodeListener = null;
multiviewTC = null;
guardedProvider = null;
guardedEditor = null;
elementToOpen = JAVA_ELEMENT_INDEX;
}
private void multiViewClosed(CloneableTopComponent mvtc) {
Enumeration en = mvtc.getReference().getComponents();
boolean isLast = !en.hasMoreElements();
if (multiviewTC == mvtc) {
multiviewTC = null;
FormDesignerTC designerTC = null;
// Find another multiviewTC, possibly with loaded formDesigner
while (en.hasMoreElements()) {
multiviewTC = (CloneableTopComponent)en.nextElement();
designerTC = getFormDesignerTC();
if (designerTC != null) {
break;
}
}
if (!isLast && (designerTC == null)) {
// Only Java elements are opened in the remaining clones
if (formEditor != null) {
formEditor.closeForm();
formEditor = null;
}
}
}
if (isLast) // last view of this form closed
notifyClosed();
}
@Override
protected boolean notifyModified () {
boolean alreadyModified = isModified();
boolean retVal = super.notifyModified();
if (retVal) { // java source modification
addSaveCookie();
}
if (!alreadyModified) {
FileObject formFile = formDataObject.getFormFile();
if (!formFile.canWrite()) { // Issue 74092
FileLock lock = null;
try {
lock = formFile.lock();
} catch (UserQuestionException uqex) {
NotifyDescriptor nd = new NotifyDescriptor.Confirmation(
uqex.getLocalizedMessage(),
FormUtils.getBundleString("TITLE_UserQuestion"), // NOI18N
NotifyDescriptor.YES_NO_OPTION);
DialogDisplayer.getDefault().notify(nd);
if (NotifyDescriptor.YES_OPTION.equals(nd.getValue())) {
try {
uqex.confirmed();
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
reloadForm();
}
});
} catch (IOException ioex) {
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioex);
}
}
} catch (IOException ex) {
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
} finally {
if (lock != null) {
lock.releaseLock();
}
}
}
updateMVTCDisplayName();
}
return retVal;
}
@Override
protected void notifyUnmodified () {
super.notifyUnmodified();
// java source modification
removeSaveCookie();
updateMVTCDisplayName();
}
private static void attachTopComponentsListener() {
if (topcompsListener != null)
return;
topcompsListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent ev) {
if (TopComponent.Registry.PROP_OPENED.equals(
ev.getPropertyName()))
{ // set of opened TopComponents has changed - hasn't some
// of our views been closed?
Set oldSet = (Set) ev.getOldValue();
Set newSet = (Set) ev.getNewValue();
if (newSet.size() < oldSet.size()) {
Iterator it = oldSet.iterator();
while (it.hasNext()) {
Object o = it.next();
if (!newSet.contains(o)) {
if (o instanceof CloneableTopComponent) {
CloneableTopComponent closedTC = (CloneableTopComponent) o;
if (getSelectedElementType(closedTC) != -1) { // it is our multiview
FormEditorSupport fes = getFormEditor(closedTC);
if (fes != null) {
fes.multiViewClosed(closedTC);
}
}
}
}
}
}
TopComponent active = TopComponent.getRegistry().getActivated();
if (active!=null && getSelectedElementType(active) != -1) { // it is our multiview
FormEditorSupport fes = getFormEditor(active);
if (fes != null) {
fes.updateMVTCDisplayName();
}
}
}
}
};
TopComponent.getRegistry().addPropertyChangeListener(topcompsListener);
}
private static void detachTopComponentsListener() {
if (topcompsListener != null) {
TopComponent.getRegistry()
.removePropertyChangeListener(topcompsListener);
topcompsListener = null;
TopComponentGroup group = WindowManager.getDefault()
.findTopComponentGroup("form"); // NOI18N
if (group != null)
group.close();
}
}
/**
* Additional updates for a newly created form before it gets loaded for the first time.
*/
private void postCreationUpdate1() {
FileObject fob = formDataObject.getPrimaryFile();
Object libName = fob.getAttribute("requiredLibrary"); // NOI18N
if (libName != null) {
Object className = fob.getAttribute("requiredClass"); // NOI18N
if ((className == null) || !ClassPathUtils.isOnClassPath(fob, className.toString())) {
try {
Library lib = LibraryManager.getDefault().getLibrary((String)libName);
ClassPathUtils.updateProject(fob, new ClassSource(
(className == null) ? null : className.toString(),
new ClassSourceResolver.LibraryEntry(lib))
);
} catch (IOException ioex) {
Logger.getLogger(FormEditorSupport.class.getName()).log(Level.INFO, ioex.getLocalizedMessage(), ioex);
}
}
}
}
/**
* Additional updates for a newly created form, just loaded for the first time.
*/
private void postCreationUpdate2() {
if (formEditor.postCreationUpdate()) {
try {
checkSuppressWarningsAnnotation();
if (isModified()) {
saveDocument();
}
} catch (IOException ex) { // unlikely for just created form
Logger.getLogger(FormEditorSupport.class.getName()).log(Level.INFO, ex.getLocalizedMessage(), ex);
}
}
}
private void checkSuppressWarningsAnnotation() throws IOException {
FileObject fo = getFormDataObject().getPrimaryFile();
String sourceLevel = SourceLevelQuery.getSourceLevel(fo);
boolean invalidSL = (sourceLevel != null) && ("1.5".compareTo(sourceLevel) > 0); // NOI18N
ClassPath cp = ClassPath.getClassPath(fo, ClassPath.BOOT);
if (invalidSL || cp.findResource("java/lang/SuppressWarnings.class") == null) { // NOI18N
// The project's bootclasspath doesn't contain SuppressWarnings class.
// So, remove this annotation from initComponents() method.
final String foName = fo.getName();
JavaSource js = JavaSource.forFileObject(fo);
final int[] positions = new int[] {-1,-1};
js.runModificationTask(new CancellableTask<WorkingCopy>() {
@Override
public void cancel() {
}
@Override
public void run(WorkingCopy wcopy) throws Exception {
wcopy.toPhase(JavaSource.Phase.RESOLVED);
ClassTree clazz = null;
CompilationUnitTree cu = wcopy.getCompilationUnit();
for (Tree tree : cu.getTypeDecls()) {
if (TreeUtilities.CLASS_TREE_KINDS.contains(tree.getKind())) {
ClassTree cand = (ClassTree)tree;
if (foName.equals(cand.getSimpleName().toString())) {
clazz = cand;
}
}
}
if (clazz == null) return;
for (Tree tree : clazz.getMembers()) {
if (tree.getKind() == Tree.Kind.METHOD) {
MethodTree method = (MethodTree)tree;
if ("initComponents".equals(method.getName().toString()) // NOI18N
&& (method.getParameters().isEmpty())) {
ModifiersTree modifiers = method.getModifiers();
for (AnnotationTree annotation : modifiers.getAnnotations()) {
if (annotation.getAnnotationType().toString().contains("SuppressWarnings")) { // NOI18N
SourcePositions sp = wcopy.getTrees().getSourcePositions();
positions[0] = (int)sp.getStartPosition(cu, annotation);
positions[1] = (int)sp.getEndPosition(cu, annotation);
// We cannot use the following code because
// part of the modifier is in guarded block
//ModifiersTree newModifiers = wcopy.getTreeMaker().removeModifiersAnnotation(method.getModifiers(), annotation);
//wcopy.rewrite(modifiers, newModifiers);
}
}
}
}
}
}
}).commit();
if (positions[0] != -1) {
try {
getFormDataObject().getFormEditorSupport().getDocument().remove(positions[0], positions[1]-positions[0]);
} catch (BadLocationException blex) {
Logger.getLogger(FormEditor.class.getName()).log(Level.INFO, blex.getLocalizedMessage(), blex);
}
}
}
}
// -------
// multiview & java editor
@Override
protected CloneableEditorSupport.Pane createPane() {
if (!formDataObject.isValid()) {
return super.createPane(); // Issue 110249
}
CloneableTopComponent mvtc = MultiViews.createCloneableMultiView("text/x-form", getDataObject());
// #45665 - dock into editor mode if possible..
Mode editorMode = WindowManager.getDefault().findMode(CloneableEditorSupport.EDITOR_MODE);
if (editorMode != null) {
editorMode.dockInto(mvtc);
}
return (CloneableEditorSupport.Pane)mvtc;
}
private static boolean readOnly(FormDataObject formDataObject) {
if (!formDataObject.getPrimaryFile().canWrite()) {
return true;
}
TopComponent active = TopComponent.getRegistry().getActivated();
if (active != null && getSelectedElementType(active) == FORM_ELEMENT_INDEX) {
FormEditorSupport fes = (FormEditorSupport)formDataObject.getFormEditorSupport();
if (fes != null) {
FormModel fm = fes.getFormModel();
if (fm != null) {
return fm.isReadOnly();
}
}
}
return false;
}
private static String getMVTCToolTipText(FormDataObject formDataObject) {
return DataEditorSupport.toolTip(formDataObject.getPrimaryFile(), formDataObject.isModified(), readOnly(formDataObject));
}
/**
* Returns display name of the multiview top component.
* The first item of the array is normal display name,
* the second item of the array is HTML display name.
*
* @param formDataObject form data object representing the multiview tc.
* @return display names of the MVTC. The second item can be <code>null</code>.
*/
private static String[] getMVTCDisplayName(FormDataObject formDataObject) {
Node node = formDataObject.getNodeDelegate();
String title = node.getDisplayName();
String htmlTitle = node.getHtmlDisplayName();
if (htmlTitle == null) {
try {
htmlTitle = XMLUtil.toElementContent(title);
} catch (CharConversionException x) {
htmlTitle = "???";
}
}
FormEditorSupport fes = (FormEditorSupport)formDataObject.getFormEditorSupport();
if (fes != null) {
FormDesignerTC designerTC = fes.getFormDesignerTC();
if (designerTC != null && designerTC.isShowing()) {
FormModel fm = fes.getFormModel();
if (fm != null) {
FormDesigner fd = FormEditor.getFormDesigner(fes.getFormModel());
if (fd != null && fd.getFormModel() != null
&& !fd.isTopRADComponent() && fd.getTopDesignComponent() != null) {
title = FormUtils.getFormattedBundleString(
"FMT_FormTitleWithContainerName", // NOI18N
new Object[] {title, fd.getTopDesignComponent().getName()});
htmlTitle = FormUtils.getFormattedBundleString(
"FMT_FormTitleWithContainerName", // NOI18N
new Object[] {htmlTitle, fd.getTopDesignComponent().getName()});
}
}
}
}
boolean modified = formDataObject.isModified();
boolean readOnly = readOnly(formDataObject);
return new String[] {
DataEditorSupport.annotateName(title, false, modified, readOnly),
DataEditorSupport.annotateName(htmlTitle, true, modified, readOnly)
};
}
@Override
protected String messageName() {
String[] titles = getMVTCDisplayName(formDataObject);
return titles[0];
}
@Override
protected String messageHtmlName() {
String[] titles = getMVTCDisplayName(formDataObject);
return titles[1];
}
/** Updates title (display name) of all multiviews for given form. Replans
* to event queue thread if necessary. */
void updateMVTCDisplayName() {
if (java.awt.EventQueue.isDispatchThread()) {
updateMVTCDisplayNameInAWT();
} else {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
updateMVTCDisplayNameInAWT();
}
});
}
}
private void updateMVTCDisplayNameInAWT() {
if ((multiviewTC == null) || (!formDataObject.isValid())) // Issue 67544
return;
String[] titles = getMVTCDisplayName(formDataObject);
Enumeration en = multiviewTC.getReference().getComponents();
while (en.hasMoreElements()) {
TopComponent tc = (TopComponent) en.nextElement();
tc.setDisplayName(titles[0]);
tc.setHtmlDisplayName(titles[1]);
}
}
/** Updates tooltip of all multiviews for given form. Replans to even queue
* thread if necessary. */
void updateMVTCToolTipText() {
if (java.awt.EventQueue.isDispatchThread()) {
if (multiviewTC == null)
return;
String tooltip = getMVTCToolTipText(formDataObject);
Enumeration en = multiviewTC.getReference().getComponents();
while (en.hasMoreElements()) {
TopComponent tc = (TopComponent) en.nextElement();
tc.setToolTipText(tooltip);
}
}
else {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
if (multiviewTC == null)
return;
String tooltip = getMVTCToolTipText(formDataObject);
Enumeration en = multiviewTC.getReference().getComponents();
while (en.hasMoreElements()) {
TopComponent tc = (TopComponent) en.nextElement();
tc.setToolTipText(tooltip);
}
}
});
}
}
@Messages({
"MSG_MODIFIED=File {0} is modified. Save?"
})
final CloseOperationState canCloseElement(TopComponent tc) {
// if this is not the last cloned java editor component, closing is OK
if (!FormEditorSupport.isLastView(tc)) {
return CloseOperationState.STATE_OK;
}
if (!isModified()) {
return CloseOperationState.STATE_OK;
}
AbstractAction save = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
try {
saveDocument();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
};
save.putValue(Action.LONG_DESCRIPTION, Bundle.MSG_MODIFIED(
getDataObject().getPrimaryFile().getNameExt()
));
// return a placeholder state - to be sure our CloseHandler is called
return MultiViewFactory.createUnsafeCloseState(
"ID_FORM_CLOSING", // NOI18N
save,
MultiViewFactory.NOOP_CLOSE_ACTION);
}
static boolean isLastView(TopComponent tc) {
if (!(tc instanceof CloneableTopComponent))
return false;
boolean oneOrLess = true;
Enumeration en = ((CloneableTopComponent)tc).getReference().getComponents();
if (en.hasMoreElements()) {
en.nextElement();
if (en.hasMoreElements())
oneOrLess = false;
}
return oneOrLess;
}
/** This is called by the multiview elements whenever they are created
* (and given a observer knowing their multiview TopComponent). It is
* important during deserialization and cloning the multiview - i.e. during
* the operations we have no control over. But anytime a multiview is
* created, this method gets called.
*/
void setTopComponent(TopComponent topComp) {
multiviewTC = (CloneableTopComponent)topComp;
String[] titles = getMVTCDisplayName(formDataObject);
multiviewTC.setDisplayName(titles[0]);
multiviewTC.setHtmlDisplayName(titles[1]);
multiviewTC.setToolTipText(getMVTCToolTipText(formDataObject));
opened.add(this);
registerNodeListener();
attachTopComponentsListener();
try {
addStatusListener(formDataObject.getPrimaryFile().getFileSystem());
} catch (FileStateInvalidException fsiex) {
fsiex.printStackTrace();
}
}
public static FormEditorSupport getFormEditor(TopComponent tc) {
Object dobj = tc.getLookup().lookup(DataObject.class);
FormEditorSupport fes = null;
if (dobj instanceof FormDataObject) {
FormDataObject formDataObject = (FormDataObject)dobj;
fes = (FormEditorSupport)formDataObject.getFormEditorSupport();
}
return fes;
}
@Override
public boolean isJavaEditorDisplayed() {
boolean showing = false;
if (EventQueue.isDispatchThread()) { // issue 91715
JEditorPane[] jeditPane = getOpenedPanes();
if (jeditPane != null) {
for (int i=0; i<jeditPane.length; i++) {
if (showing = jeditPane[i].isShowing()) {
break;
}
}
}
}
return showing;
}
/**
* Called before regenerating initComponents guarded section to obtain the
* actual state of the editor fold for the generated code. Needed for the case
* the user expanded it manually to expand it again after recreating the fold
* for the new content.
* @param offset the start offset of the initComponents section
* @return true if the fold is collapsed, false if expanded
*/
@Override
public Boolean getFoldState(int offset) {
if (EventQueue.isDispatchThread()) {
JEditorPane[] panes = getOpenedPanes();
if (panes != null && panes.length > 0) {
FoldHierarchy hierarchy = FoldHierarchy.get(panes[0]);
if (hierarchy != null) {
try {
hierarchy.lock();
Fold fold = FoldUtilities.findNearestFold(hierarchy, offset);
if (fold != null) {
return fold.isCollapsed();
}
} finally {
hierarchy.unlock();
}
}
}
}
return null;
}
/**
* Called after setting new content to the initComponents section to restore
* the remembered state of the fold. Setting the text creates a new fold that
* is initially collapsed, we may want to expand it (if the user expanded it
* manually in the editor before).
* @param collapse
* @param startOffset
* @param endOffset
*/
@Override
public void restoreFoldState(boolean collapse, int startOffset, int endOffset) {
if (collapse) {
return; // the fold will be initially collapsed
}
JEditorPane[] panes = getOpenedPanes();
if (panes != null && panes.length > 0) {
FoldHierarchy hierarchy = FoldHierarchy.get(panes[0]);
if (hierarchy != null) {
try {
hierarchy.lock();
Fold fold = FoldUtilities.findCollapsedFold(hierarchy, startOffset, endOffset);
if (fold != null) {
hierarchy.expand(fold);
} else {
// in fact we don't really know when the new fold will appear
// in the hierarchy, it happens somehow asynchronously
if (foldHierarchyListener == null) {
foldHierarchyListener = new FoldHierarchyListener();
} else {
hierarchy.removeFoldHierarchyListener(foldHierarchyListener);
}
hierarchy.addFoldHierarchyListener(foldHierarchyListener);
foldHierarchyListener.setOffsets(startOffset, endOffset);
}
} finally {
hierarchy.unlock();
}
}
}
}
private static class FoldHierarchyListener implements org.netbeans.api.editor.fold.FoldHierarchyListener {
private int startOffset = -1;
private int endOffset;
private void setOffsets(int startOffset, int endOffset) {
this.startOffset = startOffset;
this.endOffset = endOffset;
}
@Override
public void foldHierarchyChanged(FoldHierarchyEvent evt) {
if (startOffset >= 0
&& evt.getAddedFoldCount() > 0
&& evt.getAffectedStartOffset() < endOffset && evt.getAffectedEndOffset() > startOffset
&& evt.getSource() instanceof FoldHierarchy) {
// here we should have the fold for the new initComponents code added
FoldHierarchy hierarchy = (FoldHierarchy) evt.getSource();
Fold fold = FoldUtilities.findCollapsedFold(hierarchy, startOffset, endOffset);
if (fold != null) {
startOffset = -1; // ignore any futher events
hierarchy.expand(fold);
}
}
}
}
private static Boolean groupVisible = null;
static void checkFormGroupVisibility() {
// when active TopComponent changes, check if we should open or close
// the form editor group of windows (Inspector, Palette, Properties)
WindowManager wm = WindowManager.getDefault();
final TopComponentGroup group = wm.findTopComponentGroup("form"); // NOI18N
if (group == null)
return; // group not found (should not happen)
boolean designerSelected = false;
Iterator it = wm.getModes().iterator();
while (it.hasNext()) {
Mode mode = (Mode) it.next();
TopComponent selected = mode.getSelectedTopComponent();
if (getSelectedElementType(selected) == FORM_ELEMENT_INDEX) {
designerSelected = true;
break;
}
}
if (designerSelected && !Boolean.TRUE.equals(groupVisible)) {
// Bug 116008: calling group.open() first time may cause hiding the
// FormDesigner (some winsys multiview initialization mess), calling
// this method again and hiding the group. By setting the groupVisible
// to false we make the re-entrant call effectively do nothing.
groupVisible = Boolean.FALSE;
group.open();
groupVisible = Boolean.TRUE;
final TopComponentGroup paletteGroup = wm.findTopComponentGroup( "commonpalette" ); // NOI18N
if( null != paletteGroup ) {
paletteGroup.open();
}
}
else if (!designerSelected && !Boolean.FALSE.equals(groupVisible)) {
group.close();
groupVisible = Boolean.FALSE;
}
}
/** @return 0 if java editor in form editor multiview is selected
* 1 if form designer in form editor multiview is selected
* -1 if the given TopComponent is not form editor multiview
*/
static int getSelectedElementType(TopComponent tc) {
if (tc != null) {
MultiViewHandler handler = MultiViews.findMultiViewHandler(tc);
if (handler != null) {
String prefId = handler.getSelectedPerspective().preferredID();
if (MV_JAVA_ID.equals(prefId))
return JAVA_ELEMENT_INDEX; // 0
if (MV_FORM_ID.equals(prefId))
return FORM_ELEMENT_INDEX; // 1
}
}
return -1;
}
@Override
public Object getJavaContext() {
return null; // nothing else than FileObject is needed in NB
}
@Override
public int getCodeIndentSize() {
CodeStyle cs = CodeStyle.getDefault(getFormDataObject().getPrimaryFile());
return cs != null ? cs.getIndentSize() : 4;
}
@Override
public boolean getCodeBraceOnNewLine() {
CodeStyle cs = CodeStyle.getDefault(getFormDataObject().getPrimaryFile());
return cs != null ? cs.getMethodDeclBracePlacement() != CodeStyle.BracePlacement.SAME_LINE : false;
}
public SimpleSection getVariablesSection() {
return getGuardedSectionManager().findSimpleSection(SECTION_VARIABLES);
}
public SimpleSection getInitComponentSection() {
return getGuardedSectionManager().findSimpleSection(SECTION_INIT_COMPONENTS);
}
@Override
public GuardedSectionManager getGuardedSectionManager() {
try {
StyledDocument doc = null;
try {
doc = openDocument();
} catch (UserQuestionException uqex) { // Issue 143655
Object retVal = DialogDisplayer.getDefault().notify(
new NotifyDescriptor.Confirmation(uqex.getLocalizedMessage(),
NotifyDescriptor.YES_NO_OPTION));
if (NotifyDescriptor.YES_OPTION == retVal) {
uqex.confirmed();
doc = openDocument();
}
}
if (doc == null) {
// Issue 143655 - opening of big file canceled
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
close();
}
});
return null;
} else {
return GuardedSectionManager.getInstance(doc);
}
} catch (IOException ex) {
throw new IllegalStateException("cannot open document", ex); // NOI18N
}
}
@Override
public boolean canGenerateNBMnemonicsCode() {
FileObject srcFile = getFormDataObject().getPrimaryFile();
return isNBMProject(srcFile)
|| ClassPathUtils.checkUserClass("org.openide.awt.Mnemonics", srcFile); // NOI18N
}
private static boolean isNBMProject(FileObject srcFile) {
// hack: checking project impl. class name, is there a better way?
Project p = FileOwnerQuery.getOwner(srcFile);
return p != null && p.getClass().getName().startsWith("org.netbeans.modules.apisupport.") // NOI18N
&& p.getClass().getName().endsWith("Project"); // NOI18N
}
private static final class FormGEditor implements GuardedEditorSupport {
StyledDocument doc = null;
@Override
public StyledDocument getDocument() {
return doc;
}
}
private FormGEditor guardedEditor;
private GuardedSectionsProvider guardedProvider;
@Override
protected void loadFromStreamToKit(StyledDocument doc, InputStream stream, EditorKit kit) throws IOException, BadLocationException {
if (guardedEditor == null) {
guardedEditor = new FormGEditor();
GuardedSectionsFactory gFactory = GuardedSectionsFactory.find(((DataEditorSupport.Env) env).getMimeType());
if (gFactory != null) {
guardedProvider = gFactory.create(guardedEditor);
}
}
if (guardedProvider != null) {
guardedEditor.doc = doc;
Charset c = FileEncodingQuery.getEncoding(this.getDataObject().getPrimaryFile());
Reader reader = guardedProvider.createGuardedReader(stream, c);
try {
kit.read(reader, doc, 0);
} finally {
reader.close();
}
} else {
super.loadFromStreamToKit(doc, stream, kit);
}
}
@Override
protected void saveFromKitToStream(StyledDocument doc, EditorKit kit, OutputStream stream) throws IOException, BadLocationException {
if (guardedProvider != null) {
Charset c = FileEncodingQuery.getEncoding(this.getDataObject().getPrimaryFile());
Writer writer = guardedProvider.createGuardedWriter(stream, c);
try {
kit.write(writer, doc, 0, doc.getLength());
} finally {
writer.close();
}
} else {
super.saveFromKitToStream(doc, kit, stream);
}
}
@MultiViewElement.Registration(
displayName="#CTL_SourceTabCaption",
iconBase=iconURL,
persistenceType=TopComponent.PERSISTENCE_ONLY_OPENED,
preferredID=MV_JAVA_ID,
mimeType="text/x-form",
position=1000
)
public static class JavaMultiViewEditorElement extends MultiViewEditorElement {
private static final long serialVersionUID =-3126744316624172415L;
private DataObject dataObject;
private transient FormEditorSupport javaEditor;
private transient MultiViewElementCallback multiViewObserver;
public JavaMultiViewEditorElement(Lookup context) {
super(context);
dataObject = context.lookup(DataObject.class);
javaEditor = context.lookup(FormEditorSupport.class);
if (javaEditor != null) {
javaEditor.prepareDocument();
}
}
@Override
public void setMultiViewCallback(MultiViewElementCallback callback) {
multiViewObserver = callback;
// needed for deserialization...
if (dataObject instanceof FormDataObject) {
// this is used (or misused?) to obtain the deserialized
// multiview topcomponent and set it to FormEditorSupport
FormDataObject formDataObject = (FormDataObject) dataObject;
FormEditorSupport fes = (FormEditorSupport)formDataObject.getFormEditorSupport();
if (javaEditor == null) {
javaEditor = fes;
}
fes.setTopComponent(callback.getTopComponent());
}
super.setMultiViewCallback(callback);
}
@Override
public void componentShowing() {
super.componentShowing();
if (dataObject instanceof FormDataObject) {
FormDataObject formDO = (FormDataObject) dataObject;
FormEditorSupport fe = (FormEditorSupport)formDO.getFormEditorSupport();
if (fe != null) {
FormModel model = fe.getFormModel();
if (model != null) {
CodeGenerator codeGen = FormEditor.getCodeGenerator(model);
if (codeGen != null) {
codeGen.regenerateCode();
}
}
}
}
}
@Override
public CloseOperationState canCloseElement() {
if (javaEditor == null) {
return CloseOperationState.STATE_OK;
}
return javaEditor.canCloseElement(multiViewObserver.getTopComponent());
}
}
private static final class Environment extends DataEditorSupport.Env {
private static final long serialVersionUID = -1;
public Environment(DataObject obj) {
super(obj);
}
@Override
protected FileObject getFile() {
return this.getDataObject().getPrimaryFile();
}
@Override
protected FileLock takeLock() throws java.io.IOException {
return ((FormDataObject) getDataObject()).getPrimaryEntry().takeLock();
}
@Override
public CloneableOpenSupport findCloneableOpenSupport() {
return this.getDataObject().getCookie(FormEditorSupport.class);
}
}
private final SaveCookie saveCookie = new SaveCookie() {
@Override
public void save() throws java.io.IOException {
if (formEditor == null) { // not saving form, only java
doSave(false); // don't need to be in event dispatch thread (#102986)
} else if (EventQueue.isDispatchThread()) {
doSave(true);
} else {
try {
EventQueue.invokeAndWait(new Runnable() {
@Override
public void run() {
doSave(true);
}
});
} catch (InterruptedException ex) {
Logger.getLogger(FormEditorSupport.class.getName()).log(Level.INFO, "", ex); // NOI18N
} catch (InvocationTargetException ex) {
if (ex.getCause() instanceof RuntimeException) {
throw (RuntimeException) ex.getCause();
}
Logger.getLogger(FormEditorSupport.class.getName()).log(Level.INFO, "", ex); // NOI18N
}
}
}
private void doSave(boolean bothJavaAndForm) {
try {
if (bothJavaAndForm) {
saveDocument();
} else {
saveSourceOnly();
}
} catch (IOException ex) {
Logger.getLogger(FormEditorSupport.class.getName()).log(Level.INFO, "", ex); // NOI18N
}
}
};
private final CookieSet cookies;
public void addSaveCookie() {
DataObject javaData = this.getDataObject();
if (javaData.getCookie(SaveCookie.class) == null) {
cookies.add(saveCookie);
javaData.setModified(true);
}
}
public void removeSaveCookie() {
DataObject javaData = this.getDataObject();
if (javaData.getCookie(SaveCookie.class) != null) {
cookies.remove(saveCookie);
javaData.setModified(false);
}
}
}