| /* |
| * 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); |
| } |
| } |
| |
| } |