| /******************************************************************************* |
| * Copyright (c) 2006, 2015 Sybase, Inc. and others. |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Sybase, Inc. - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jst.pagedesigner.editors; |
| |
| import java.io.File; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtension; |
| import org.eclipse.core.runtime.IExtensionPoint; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.content.IContentDescription; |
| import org.eclipse.core.runtime.content.IContentType; |
| import org.eclipse.gef.DefaultEditDomain; |
| import org.eclipse.gef.EditPart; |
| import org.eclipse.gef.ui.views.palette.PalettePage; |
| import org.eclipse.gef.ui.views.palette.PaletteViewerPage; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.TextSelection; |
| import org.eclipse.jface.viewers.IPostSelectionProvider; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jst.jsf.common.ui.internal.logging.Logger; |
| import org.eclipse.jst.jsf.common.ui.internal.utils.ResourceUtils; |
| import org.eclipse.jst.pagedesigner.IJMTConstants; |
| import org.eclipse.jst.pagedesigner.PDPlugin; |
| import org.eclipse.jst.pagedesigner.dnd.internal.DesignerSourceMouseTrackAdapter; |
| import org.eclipse.jst.pagedesigner.editors.IWPEPersistenceListener.IPersistenceEvent; |
| import org.eclipse.jst.pagedesigner.editors.IWPEPersistenceListener.PersistenceEventType; |
| import org.eclipse.jst.pagedesigner.editors.actions.DesignPageActionContributor; |
| import org.eclipse.jst.pagedesigner.editors.pagedesigner.PageDesignerResources; |
| import org.eclipse.jst.pagedesigner.jsp.core.pagevar.IPageVariablesProvider; |
| import org.eclipse.jst.pagedesigner.jsp.core.pagevar.adapter.IDocumentPageVariableAdapter; |
| import org.eclipse.jst.pagedesigner.parts.DocumentEditPart; |
| import org.eclipse.jst.pagedesigner.preview.PreviewHandlerNew; |
| import org.eclipse.jst.pagedesigner.preview.WindowsIEBrowser; |
| import org.eclipse.jst.pagedesigner.properties.WPETabbedPropertySheetPage; |
| import org.eclipse.jst.pagedesigner.tools.RangeSelectionTool; |
| import org.eclipse.jst.pagedesigner.ui.common.PartActivationHandler; |
| import org.eclipse.jst.pagedesigner.ui.common.sash.SashEditorPart; |
| import org.eclipse.jst.pagedesigner.ui.preferences.PDPreferences; |
| import org.eclipse.jst.pagedesigner.utils.EditorUtil; |
| import org.eclipse.jst.pagedesigner.utils.PreviewUtil; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.FillLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IPropertyListener; |
| import org.eclipse.ui.IStorageEditorInput; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.contexts.IContextService; |
| import org.eclipse.ui.part.FileEditorInput; |
| import org.eclipse.ui.part.MultiPageEditorPart; |
| import org.eclipse.ui.part.MultiPageEditorSite; |
| import org.eclipse.ui.part.MultiPageSelectionProvider; |
| import org.eclipse.ui.texteditor.IDocumentProvider; |
| import org.eclipse.ui.views.contentoutline.IContentOutlinePage; |
| import org.eclipse.ui.views.properties.IPropertySheetPage; |
| import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor; |
| import org.eclipse.wst.sse.core.StructuredModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; |
| import org.eclipse.wst.sse.ui.StructuredTextEditor; |
| import org.eclipse.wst.sse.ui.internal.provisional.extensions.ISourceEditingTextTools; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.eclipse.wst.xml.ui.internal.provisional.IDOMSourceEditingTextTools; |
| import org.w3c.dom.Document; |
| |
| /** |
| * The HTMLEditor is a multi paged editor. It will use the StructuredTextEditor |
| * as the chief editor, and delegate most operations to it. |
| * |
| * @author mengbo |
| */ |
| public final class HTMLEditor extends MultiPageEditorPart implements |
| IPropertyListener, ITabbedPropertySheetPageContributor { |
| // private static final String PAGE_NAME_DESIGN = "Design"; //$NON-NLS-1$ |
| // private static final String PAGE_NAME_SOURCE = "Source"; //$NON-NLS-1$ |
| /** |
| * Tabbed property contributor id for WPE |
| */ |
| public final static String TABBED_PROPERTIES_CONTRIBUTOR_ID = "org.eclipse.jst.pagedesigner.tabPropertyContributor"; //$NON-NLS-1$ |
| |
| // four different modes for the designer when displayed in a sash editor. |
| /** |
| * editor split is vertical |
| */ |
| public static final int MODE_SASH_VERTICAL = 0; |
| |
| /** |
| * editor split is horizontal |
| */ |
| public static final int MODE_SASH_HORIZONTAL = 1; |
| |
| /** |
| * no split, only designer canvas |
| */ |
| public static final int MODE_DESIGNER = 2; |
| |
| /** |
| * no split, only SSE source |
| */ |
| public static final int MODE_SOURCE = 3; |
| |
| private Logger _log = PDPlugin.getLogger(HTMLEditor.class); |
| |
| private boolean _sash = true; |
| |
| private int _mode = 0; |
| |
| private SashEditorPart _sashEditorPart = null; |
| |
| private int _previewPageIndex = -1; |
| |
| /** The design viewer */ |
| private SimpleGraphicalEditor _designViewer; |
| |
| /** The text editor. */ |
| private StructuredTextEditor _textEditor; |
| |
| private PartActivationHandler _partListener; |
| |
| private PaletteViewerPage _paletteViewerPage; |
| |
| private DefaultEditDomain _editDomain; |
| |
| private WindowsIEBrowser _browser; |
| |
| private Composite _previewComposite; |
| |
| private List PREVIEW_FILES_LIST = new ArrayList(); |
| |
| private IPropertySheetPage _tabbedPropSheet; |
| |
| private ISelectionChangedListener _selChangedListener; |
| |
| private DesignPageActionContributor _designPageActionContributor; |
| |
| private IStructuredModel _model; |
| |
| // TODO:This class is never used locally |
| // private class TextInputListener implements ITextInputListener { |
| // public void inputDocumentAboutToBeChanged(IDocument oldInput, |
| // IDocument newInput) { |
| // // do nothing |
| // } |
| // |
| // public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { |
| // if (_designViewer != null && newInput != null) |
| // _designViewer.setModel(getModel()); |
| // } |
| // } |
| |
| private List<IWPEPersistenceListener> persistenceListeners; |
| |
| /** |
| * Default constructor |
| */ |
| public HTMLEditor() { |
| super(); |
| } |
| |
| /* |
| * This method is just to make firePropertyChanged accessbible from some |
| * (anonomous) inner classes. |
| */ |
| private void _firePropertyChange(int property) { |
| super.firePropertyChange(property); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see com.ibm.xtools.common.ui.properties.ITabbedPropertySheetPageContributor#getContributorId() |
| */ |
| public String getContributorId() { |
| return TABBED_PROPERTIES_CONTRIBUTOR_ID; |
| } |
| |
| private void connectSashPage() { |
| ISelectionProvider selectionProvider = _sashEditorPart.getSite() |
| .getSelectionProvider(); |
| if (selectionProvider instanceof IPostSelectionProvider) { |
| ((IPostSelectionProvider) selectionProvider) |
| .addPostSelectionChangedListener(getSelectionChangedListener(selectionProvider)); |
| } else { |
| selectionProvider |
| .addSelectionChangedListener(getSelectionChangedListener(selectionProvider)); |
| } |
| } |
| |
| private void disconnectSashPage() { |
| //attempted fix for bug 283569... was not able to repro, but should protect against NPE |
| if (_sashEditorPart != null |
| && _sashEditorPart.getSite() != null |
| && _sashEditorPart.getSite().getSelectionProvider() != null |
| && _selChangedListener != null) { |
| |
| final ISelectionProvider selectionProvider = _sashEditorPart.getSite() |
| .getSelectionProvider(); |
| if (selectionProvider != null) { |
| if (selectionProvider instanceof IPostSelectionProvider) { |
| ((IPostSelectionProvider) selectionProvider) |
| .removePostSelectionChangedListener(getSelectionChangedListener(selectionProvider)); |
| } else { |
| selectionProvider |
| .removeSelectionChangedListener(getSelectionChangedListener(selectionProvider)); |
| } |
| } |
| } |
| } |
| |
| private ISelectionChangedListener getSelectionChangedListener(ISelectionProvider selectionProvider) { |
| if (_selChangedListener == null) { |
| if (selectionProvider instanceof IPostSelectionProvider) { |
| _selChangedListener = new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| ((MultiPageSelectionProvider) getSite() |
| .getSelectionProvider()) |
| .firePostSelectionChanged(event); |
| } |
| }; |
| } |
| else { |
| _selChangedListener = new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| ((MultiPageSelectionProvider) getSite() |
| .getSelectionProvider()) |
| .firePostSelectionChanged(event); |
| } |
| }; |
| } |
| } |
| return _selChangedListener; |
| } |
| |
| /** |
| * Creates the source page of the multi-page editor. |
| * @throws PartInitException |
| */ |
| protected void sash_createAndAddDesignSourcePage() throws PartInitException { |
| // create source page |
| _textEditor = createTextEditor(); |
| _textEditor.setEditorPart(this); |
| _textEditor.addPropertyListener(this); |
| // create design page |
| _designViewer = new SimpleGraphicalEditor(this, getEditDomain()); |
| |
| // create SashEditor |
| _sashEditorPart = new SashEditorPart() { |
| protected void createPages() throws PartInitException { |
| addPage(_designViewer, getEditorInput()); |
| addPage(_textEditor, getEditorInput()); |
| } |
| }; |
| int sashIndex = addPage(_sashEditorPart, getEditorInput()); |
| |
| // Set the sash editor mode from the stored file property |
| // or the default preference |
| initDesignerMode(); |
| |
| setPageText(sashIndex, PDPlugin.getResourceString("HTMLEditor.Design")); //$NON-NLS-1$ |
| |
| // the update's critical, to get viewer selection manager and |
| // highlighting to work |
| _textEditor.update(); |
| |
| firePropertyChange(PROP_TITLE); |
| |
| // Changes to the Text Viewer's document instance should also force an |
| // input refresh |
| // _textEditor.getTextViewer().addTextInputListener(new |
| // TextInputListener()); |
| connectSashPage(); |
| } |
| |
| /** |
| * @see org.eclipse.ui.part.MultiPageEditorPart#createSite(org.eclipse.ui.IEditorPart) |
| */ |
| protected IEditorSite createSite(IEditorPart editor) { |
| return new MultiPageEditorSite(this, editor); |
| } |
| |
| private void tabbed_createAndAddDesignSourcePage() |
| throws PartInitException { |
| // create source page |
| _textEditor = createTextEditor(); |
| _textEditor.setEditorPart(this); |
| _textEditor.addPropertyListener(this); |
| |
| // create design page |
| SimpleGraphicalEditor editor = new SimpleGraphicalEditor(this, |
| getEditDomain()); |
| |
| // add design page |
| int designPageIndex = addPage(editor, null); |
| |
| _designViewer = editor; |
| // // note: By adding the design page as a Control instead of an |
| // // IEditorPart, page switches will indicate |
| // // a "null" active editor when the design page is made active |
| setPageText(designPageIndex, PDPlugin |
| .getResourceString("HTMLEditor.Design")); //$NON-NLS-1$ |
| |
| // add source page |
| int sourcePageIndex = addPage(_textEditor, getEditorInput()); |
| setPageText(sourcePageIndex, PDPlugin |
| .getResourceString("HTMLEditor.Source")); //$NON-NLS-1$ |
| // the update's critical, to get viewer selection manager and |
| // highlighting to work |
| _textEditor.update(); |
| |
| firePropertyChange(PROP_TITLE); |
| |
| // Changes to the Text Viewer's document instance should also force an |
| // input refresh |
| // _textEditor.getTextViewer().addTextInputListener(new |
| // TextInputListener()); |
| } |
| |
| private void createAndAddPreviewPage() { |
| _previewComposite = new Composite(getContainer(), 0); |
| FillLayout filllayout = new FillLayout(); |
| _previewComposite.setLayout(filllayout); |
| |
| _previewPageIndex = addPage(_previewComposite); |
| // JSPSourceEditor.Page.Preview.PageText=Preview |
| setPageText(_previewPageIndex, PageDesignerResources.getInstance() |
| .getString("JSPSourceEditor.Page.Preview.PageText")); //$NON-NLS-1$ |
| |
| } |
| |
| private WindowsIEBrowser getPreviewBrowser() { |
| if (_browser == null) { |
| _browser = new WindowsIEBrowser(); |
| if (_browser != null) { |
| _browser.create(_previewComposite, SWT.NONE); |
| _previewComposite.layout(); |
| } |
| } |
| return _browser; |
| } |
| /** |
| * Connects the design viewer with the viewer selection manager. Should be |
| * done after createSourcePage() is done because we need to get the |
| * ViewerSelectionManager from the TextEditor. setModel is also done here |
| * because getModel() needs to reference the TextEditor. |
| */ |
| protected void connectDesignPage() { |
| if (_designViewer != null) { |
| _designViewer.setModel(getModel()); |
| // _designViewer.getSynchronizer().listenToModel(getModel()); |
| ISelectionProvider designSelectionProvider = _designViewer |
| .getSite().getSelectionProvider(); |
| if (designSelectionProvider instanceof IPostSelectionProvider) { |
| ((IPostSelectionProvider) designSelectionProvider) |
| .addPostSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged( |
| SelectionChangedEvent event) { |
| if (getActiveEditor() != _textEditor) { |
| _designViewer.getSynchronizer() |
| .selectionChanged(event); |
| } |
| } |
| }); |
| } else { |
| designSelectionProvider |
| .addSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged( |
| SelectionChangedEvent event) { |
| if (getActiveEditor() != _textEditor) { |
| _designViewer.getSynchronizer() |
| .selectionChanged(event); |
| } |
| } |
| }); |
| } |
| ISelectionProvider textSelectionProvider = _textEditor.getSite() |
| .getSelectionProvider(); |
| if (textSelectionProvider instanceof IPostSelectionProvider) { |
| ((IPostSelectionProvider) textSelectionProvider) |
| .addPostSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged( |
| SelectionChangedEvent event) { |
| if (event.getSelection() instanceof TextSelection) { |
| TextSelection textSelection = ((TextSelection) event |
| .getSelection()); |
| _designViewer |
| .getSynchronizer() |
| .textSelectionChanged( |
| textSelection.getOffset(), |
| textSelection.getOffset() |
| + textSelection |
| .getLength()); |
| } |
| } |
| }); |
| } else { |
| textSelectionProvider |
| .addSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged( |
| SelectionChangedEvent event) { |
| TextSelection textSelection = ((TextSelection) event |
| .getSelection()); |
| _designViewer.getSynchronizer() |
| .textSelectionChanged( |
| textSelection.getOffset(), |
| textSelection.getOffset() |
| + textSelection |
| .getLength()); |
| } |
| }); |
| } |
| } |
| } |
| |
| /** |
| * Creates the pages of this multi-page editor. |
| * <p> |
| * Subclasses of <code>MultiPageEditor</code> must implement this method. |
| * </p> |
| */ |
| protected void createPages() { |
| try { |
| // source page MUST be created before design page, now |
| if (_sash) { |
| sash_createAndAddDesignSourcePage(); |
| } else { |
| tabbed_createAndAddDesignSourcePage(); |
| } |
| connectDesignPage(); |
| |
| //show preview page unless preference is hiding for content type |
| boolean showPreviewPage = true; |
| final IEditorInput input = getEditorInput(); |
| if (input instanceof FileEditorInput) { |
| final IFile file = ((FileEditorInput)input).getFile(); |
| if (file != null) { |
| try { |
| final IContentDescription description = file.getContentDescription(); |
| if (description != null) { |
| final IContentType type = description.getContentType(); |
| if (type != null) { |
| final String id = type.getId(); |
| if (id != null && id.length() > 0) { |
| if (Arrays.binarySearch(PDPreferences.getHiddenPreviewPageContentTypes(), id) > -1) { |
| showPreviewPage = false; |
| } |
| } |
| } |
| } |
| } catch (CoreException cEx) { |
| //do nothing |
| } |
| } |
| } |
| if (showPreviewPage) { |
| createAndAddPreviewPage(); |
| } |
| |
| DesignerSourceMouseTrackAdapter adapter = new DesignerSourceMouseTrackAdapter( |
| _textEditor, getEditDomain()); |
| _textEditor.getTextViewer().getTextWidget().addMouseListener( |
| adapter); |
| _textEditor.getTextViewer().getTextWidget().addMouseMoveListener( |
| adapter); |
| } catch (PartInitException exception) { |
| //$NON-NLS-1$ = "An error has occurred when initializing the input for the the editor's source page." |
| if (_log != null) { |
| // throw new SourceEditingRuntimeException( |
| // "An error has occurred when initializing the input for the |
| // the editor's source page."); |
| } |
| } |
| // TODO: add a catch block here for any exception the design |
| // page throws and convert it into a more informative message. |
| } |
| |
| /** |
| * Method createTextEditor. |
| * |
| * @return StructuredTextEditor |
| */ |
| protected StructuredTextEditor createTextEditor() { |
| return new DesignerStructuredTextEditorJSP() { |
| @Override |
| protected void performRevert() { |
| if (firePersistenceEvent(PersistenceEventType.BEFORE_REVERT)) { |
| super.performRevert(); |
| firePersistenceEvent(PersistenceEventType.REVERTED); |
| } |
| } |
| }; |
| } |
| |
| private void disconnectDesignPage() { |
| if (_designViewer != null) { |
| _designViewer.setModel(null); |
| _designViewer.dispose(); |
| } |
| } |
| |
| public void dispose() { |
| //System.out.println("dispose of HTML Editor"); |
| deletePreviewFiles(); |
| |
| disconnectSashPage(); |
| disconnectDesignPage(); |
| |
| IWorkbenchWindow window = getSite().getWorkbenchWindow(); |
| if (_partListener != null) { |
| window.getPartService().removePartListener(_partListener); |
| window.getShell().removeShellListener(_partListener); |
| getSite().getPage().removePartListener(_partListener); |
| } |
| |
| if (_textEditor != null) { |
| _textEditor.removePropertyListener(this); |
| _textEditor.setEditorPart(null); |
| _textEditor.dispose(); |
| } |
| |
| // moved to last when added window ... seems like |
| // we'd be in danger of losing some data, like site, |
| // or something. |
| _sashEditorPart = null; |
| _tabbedPropSheet = null; |
| _partListener = null; |
| _editDomain = null; |
| _designViewer = null; |
| _browser = null; |
| _previewComposite = null; |
| _paletteViewerPage = null; |
| _log = null; |
| _selChangedListener = null; |
| _textEditor = null; |
| |
| if (_model != null) { |
| _model.releaseFromEdit(); |
| _model = null; |
| } |
| |
| if (persistenceListeners != null) { |
| persistenceListeners.clear(); |
| persistenceListeners = null; |
| } |
| |
| super.dispose(); |
| |
| } |
| |
| public void doSave(IProgressMonitor monitor) { |
| if (firePersistenceEvent(PersistenceEventType.BEFORE_SAVE)) { |
| _textEditor.doSave(monitor); |
| firePersistenceEvent(PersistenceEventType.SAVED); |
| } else { |
| monitor.setCanceled(true); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) Saves the contents of this editor to another object. <p> |
| * Subclasses must override this method to implement the open-save-close |
| * lifecycle for an editor. For greater details, see <code> IEditorPart |
| * </code></p> |
| * |
| * @see IEditorPart |
| */ |
| public void doSaveAs() { |
| if (firePersistenceEvent(PersistenceEventType.BEFORE_SAVE_AS)) { |
| _textEditor.doSaveAs(); |
| firePersistenceEvent(PersistenceEventType.SAVED_AS); |
| } |
| } |
| |
| private void editorInputIsAcceptable(IEditorInput input) |
| throws PartInitException { |
| if (input instanceof IFileEditorInput) { |
| // verify that it can be opened |
| CoreException[] coreExceptionArray = new CoreException[1]; |
| if (fileDoesNotExist((IFileEditorInput) input, coreExceptionArray)) { |
| // todo use message formatter for {0} |
| Throwable coreException = coreExceptionArray[0]; |
| |
| // C.B: this is a strange piece of logic. It was referenceing |
| // the internal sub-class of CoreException, ResourceException. |
| // need to review fileDoesNotExist. |
| if (coreException instanceof CoreException) { |
| // I'm assuming this is always 'does not exist' |
| // we'll refresh local go mimic behavior of default |
| // editor, where the |
| // troublesome file is refreshed (and will cause it to |
| // 'disappear' from Navigator. |
| try { |
| ((IFileEditorInput) input).getFile() |
| .refreshLocal(IResource.DEPTH_ZERO, |
| new NullProgressMonitor()); |
| } catch (CoreException ce) { |
| if (_log != null) { |
| _log.error("Error.HTMLEditor.0", ce); //$NON-NLS-1$ |
| } |
| } |
| throw new PartInitException("Resource " + input.getName() //$NON-NLS-1$ |
| + " does not exist."); //$NON-NLS-1$ |
| } |
| throw new PartInitException("Editor could not be open on " //$NON-NLS-1$ |
| + input.getName()); |
| } |
| } else if (input instanceof IStorageEditorInput) { |
| InputStream contents = null; |
| try { |
| contents = ((IStorageEditorInput) input).getStorage() |
| .getContents(); |
| if (contents == null) { |
| throw new PartInitException("Editor could not be open on " //$NON-NLS-1$ |
| + input.getName()); |
| } |
| } catch (CoreException noStorageExc) { |
| // Error in geting storage contents |
| _log.error("Error.HTMLEditor.1", noStorageExc); //$NON-NLS-1$ |
| } |
| finally |
| { |
| ResourceUtils.ensureClosed(contents); |
| } |
| } |
| } |
| |
| /** |
| * Initializes the editor part with a site and input. <p> |
| * Subclasses of <code> EditorPart </code> must implement this method. |
| * Within the implementation subclasses should verify that the input type is |
| * acceptable and then save the site and input. Here is sample code: </p><pre> |
| * if (!(input instanceof IFileEditorInput)) throw new |
| * PartInitException("Invalid Input: Must be IFileEditorInput"); |
| * setSite(site); setInput(editorInput); </pre> |
| * @param input |
| * @param coreException |
| * @return true if the input doesn't exist |
| */ |
| protected boolean fileDoesNotExist(IFileEditorInput input, |
| Throwable[] coreException) { |
| boolean result = false; |
| InputStream inStream = null; |
| if ((!(input.exists())) || (!(input.getFile().exists()))) { |
| result = true; |
| } else { |
| try |
| { |
| inStream = input.getFile().getContents(true); |
| } |
| catch (CoreException e) |
| { |
| // very likely to be file not found |
| result = true; |
| coreException[0] = e; |
| // The core has exception |
| _log.error("Error.HTMLEditor.3", e); //$NON-NLS-1$ |
| } |
| finally |
| { |
| ResourceUtils.ensureClosed(inStream); |
| } |
| } |
| return result; |
| } |
| |
| public Object getAdapter(Class key) { |
| Object result = null; |
| if (key == IDesignViewer.class) { |
| result = _designViewer; |
| } else if (key == PalettePage.class) { |
| return getPaletteViewerPage(); |
| } else if (key == IPropertySheetPage.class) { |
| // XXX: we can delegate this to the fTextEditor, but that use some |
| // more |
| // complicate mechanism, and don't work with page designer well, so |
| // do it simple now, fix later. |
| // return _textEditor.getAdapter(key); |
| return getPropertySheetPage(); |
| } else if (key == IContentOutlinePage.class) { |
| if (_textEditor != null) { |
| result = _textEditor.getAdapter(key); |
| } |
| } else if (key == IPageVariablesProvider.class) { |
| Object obj = ((IDOMModel)getModel()).getDocument().getAdapterFor( |
| IDocumentPageVariableAdapter.class); |
| if (obj instanceof IPageVariablesProvider) { |
| return obj; |
| } |
| return null; |
| } else { |
| // DMW: I'm bullet-proofing this because |
| // its been reported (on 4.03 version) a null pointer sometimes |
| // happens here on startup, when an editor has been left |
| // open when workbench shutdown. |
| if (_textEditor != null) { |
| result = _textEditor.getAdapter(key); |
| } |
| } |
| return result; |
| } |
| |
| |
| /** |
| * IExtendedSimpleEditor method |
| * @return IDocument |
| */ |
| public IDocument getDocument() { |
| if (getTextEditor() == null) { |
| return null; |
| } |
| |
| Object apapter = _textEditor.getAdapter(ISourceEditingTextTools.class); |
| if (apapter != null) { |
| return ((ISourceEditingTextTools) apapter).getDocument(); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * IExtendedMarkupEditor method |
| * @return the dom document |
| */ |
| public Document getDOMDocument() { |
| if (getTextEditor() == null) { |
| return null; |
| } |
| |
| Object adapter = _textEditor.getAdapter(ISourceEditingTextTools.class); |
| if (adapter instanceof IDOMSourceEditingTextTools) { |
| return ((IDOMSourceEditingTextTools) adapter).getDOMDocument(); |
| } |
| return null; |
| } |
| |
| /** |
| * IExtendedSimpleEditor method |
| * @return the editor part |
| */ |
| public IEditorPart getEditorPart() { |
| return this; |
| } |
| |
| /** |
| * Caller MUST NOT release this model, it will be released in {@link #dispose()}. |
| * @return the structured model |
| */ |
| public IStructuredModel getModel() { |
| if (_model == null) { |
| if (_textEditor != null) { |
| IDocumentProvider documentProvider = _textEditor.getDocumentProvider(); |
| if (documentProvider != null) { |
| IDocument document = documentProvider.getDocument(_textEditor.getEditorInput()); |
| if (document instanceof IStructuredDocument) { |
| IModelManager modelManager = StructuredModelManager.getModelManager(); |
| if (modelManager != null) { |
| _model = modelManager.getExistingModelForEdit(document); |
| if (_model == null) { |
| _model = modelManager.getModelForEdit((IStructuredDocument)document); |
| } |
| } |
| } |
| } |
| } |
| } |
| return _model; |
| } |
| |
| |
| /** |
| * @return the SSE editor delegate |
| */ |
| public StructuredTextEditor getTextEditor() { |
| return _textEditor; |
| } |
| |
| |
| /* |
| * (non-Javadoc) Method declared on IWorkbenchPart. |
| */ |
| public String getTitle() { |
| String title = null; |
| if (getTextEditor() == null) { |
| if (getEditorInput() != null) { |
| title = getEditorInput().getName(); |
| } |
| } else { |
| title = getTextEditor().getTitle(); |
| } |
| if (title == null) { |
| title = getPartName(); |
| } |
| return title; |
| } |
| |
| public void init(IEditorSite site, IEditorInput input) |
| throws PartInitException { |
| super.init(site, input); |
| editorInputIsAcceptable(input); |
| try { |
| // super.init(site, input); |
| // setSite(site); |
| setInput(input); |
| if (_partListener == null) { |
| _partListener = new PartActivationHandler(this) { |
| public void handleActivation() { |
| safelySanityCheckState(); |
| } |
| }; |
| } |
| // we want to listen for our own activation |
| IWorkbenchWindow window = getSite().getWorkbenchWindow(); |
| window.getPartService().addPartListener(_partListener); |
| window.getShell().addShellListener(_partListener); |
| |
| // TODO: is this the right place to do this? |
| // enable our editor context |
| IContextService contextService = (IContextService) getSite() |
| .getService(IContextService.class); |
| contextService.activateContext("org.eclipse.jst.pagedesigner.editorContext"); //$NON-NLS-1$ |
| |
| } catch (Exception e) { |
| // Error in editor initialization |
| _log.error("Error.HTMLEditor.5", e); //$NON-NLS-1$ |
| } |
| setPartName(input.getName()); |
| } |
| |
| /* |
| * (non-Javadoc) Returns whether the "save as" operation is supported by |
| * this editor. <p> Subclasses must override this method to implement the |
| * open-save-close lifecycle for an editor. For greater details, see <code> |
| * IEditorPart </code></p> |
| * |
| * @see IEditorPart |
| */ |
| public boolean isSaveAsAllowed() { |
| return _textEditor != null && _textEditor.isSaveAsAllowed(); |
| } |
| |
| /* |
| * (non-Javadoc) Returns whether the contents of this editor should be saved |
| * when the editor is closed. <p> This method returns <code> true </code> if |
| * and only if the editor is dirty ( <code> isDirty </code> ). </p> |
| */ |
| public boolean isSaveOnCloseNeeded() { |
| // overriding super class since it does a lowly isDirty! |
| if (_textEditor != null) { |
| return _textEditor.isSaveOnCloseNeeded(); |
| } |
| return isDirty(); |
| } |
| |
| /** |
| * Posts the update code "behind" the running operation. |
| */ |
| private void postOnDisplayQue(Runnable runnable) { |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| IWorkbenchWindow[] windows = workbench.getWorkbenchWindows(); |
| if (windows != null && windows.length > 0) { |
| Display display = windows[0].getShell().getDisplay(); |
| display.asyncExec(runnable); |
| } else { |
| runnable.run(); |
| } |
| } |
| |
| /** |
| * Indicates that a property has changed. |
| * |
| * @param source |
| * the object whose property has changed |
| * @param propId |
| * the id of the property which has changed; property ids are |
| * generally defined as constants on the source class |
| */ |
| public void propertyChanged(Object source, int propId) { |
| switch (propId) { |
| // had to implement input changed "listener" so that |
| // strucutedText could tell it containing editor that |
| // the input has change, when a 'resource moved' event is |
| // found. |
| case IEditorPart.PROP_INPUT: { |
| if (source == _textEditor) { |
| if (_textEditor.getEditorInput() != getEditorInput()) { |
| //Bug 392859 - [Regression] Incorrect WPE model returned from HTMLEditor after page name change. |
| // release the old model |
| if (_model != null) { |
| _model.releaseFromEdit(); |
| _model = null; |
| } |
| setInput(_textEditor.getEditorInput()); |
| // title should always change when input changes. |
| // create runnable for following post call |
| Runnable runnable = new Runnable() { |
| public void run() { |
| _firePropertyChange(IWorkbenchPart.PROP_TITLE); |
| } |
| }; |
| // Update is just to post things on the display queue |
| // (thread). We have to do this to get the dirty |
| // property to get updated after other things on the |
| // queue are executed. |
| postOnDisplayQue(runnable); |
| } |
| } |
| break; |
| } |
| case IWorkbenchPart.PROP_TITLE: { |
| // // update the input if the title is changed. why? It seems input |
| // change event will be fired at last. |
| // if (source == _textEditor) |
| // { |
| // if (_textEditor.getEditorInput() != getEditorInput()) |
| // { |
| // setInput(_textEditor.getEditorInput()); |
| // } |
| // } |
| // break; |
| } |
| default: { |
| // propagate changes. Is this needed? Answer: Yes. |
| // PROP_PART_NAME, PROP_DIRTY etc. |
| if (source == _textEditor) { |
| firePropertyChange(propId); |
| } |
| break; |
| } |
| } |
| |
| } |
| |
| private void safelySanityCheckState() { |
| // If we're called before editor is created, simply ignore since we |
| // delegate this function to our embedded TextEditor |
| if (getTextEditor() == null) { |
| return; |
| } |
| |
| getTextEditor().safelySanityCheckState(getEditorInput()); |
| |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.part.EditorPart#setInput(org.eclipse.ui.IEditorInput) |
| */ |
| protected void setInput(IEditorInput input) { |
| // If driven from the Source page, it's "model" may not be up to date |
| // with the input just yet. We'll rely on later notification from the |
| // TextViewer to set us straight |
| super.setInput(input); |
| if (_designViewer != null) { |
| _designViewer.setModel(getModel()); |
| } |
| setPartName(input.getName()); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.part.EditorPart#isDirty() |
| */ |
| public boolean isDirty() { |
| if (getTextEditor() == null) { |
| return false; |
| } |
| return getTextEditor().isDirty(); |
| } |
| |
| private IPropertySheetPage getPropertySheetPage() |
| { |
| if (_tabbedPropSheet == null || _tabbedPropSheet.getControl() == null |
| || _tabbedPropSheet.getControl().isDisposed()) |
| { |
| IPropertySheetPageFactory factory = getPageFactory(); |
| if (factory != null) |
| { |
| final IFile file = ((IFileEditorInput)getEditorInput()).getFile(); |
| _tabbedPropSheet = factory.createPage(file); |
| } |
| else |
| { |
| _tabbedPropSheet = new WPETabbedPropertySheetPage(this,this); |
| } |
| } |
| return _tabbedPropSheet; |
| } |
| |
| private IPropertySheetPageFactory getPageFactory() |
| { |
| //List<IElementEditFactory> result = new ArrayList<IElementEditFactory>(); |
| IExtensionPoint extensionPoint = Platform.getExtensionRegistry() |
| .getExtensionPoint(PDPlugin.getPluginId(), |
| IJMTConstants.EXTENSION_POINT_PAGEDESIGNER); |
| IExtension[] extensions = extensionPoint.getExtensions(); |
| |
| for (int i = 0; i < extensions.length; i++) |
| { |
| IExtension ext = extensions[i]; |
| IConfigurationElement[] elementEditElement = ext |
| .getConfigurationElements(); |
| |
| for (int j = 0; j < elementEditElement.length; j++) |
| { |
| final IConfigurationElement element = elementEditElement[j]; |
| if (element.getName().equals( |
| IJMTConstants.PROPERTY_PAGE_FACTORY)) |
| { |
| elementEditElement[j].getAttribute("class"); //$NON-NLS-1$ |
| Object obj; |
| try |
| { |
| obj = elementEditElement[j] |
| .createExecutableExtension("class"); //$NON-NLS-1$ |
| |
| // TODO: we need a policy based solution here, |
| // but this will do for now |
| if (obj instanceof IPropertySheetPageFactory) |
| { |
| return (IPropertySheetPageFactory) obj; |
| } |
| } |
| catch (CoreException e) |
| { |
| PDPlugin.log("Problem loading element edit extension for "+element.toString(), e); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @return PaletteViewerPage |
| */ |
| private PaletteViewerPage getPaletteViewerPage() |
| { |
| if (_paletteViewerPage == null) |
| { |
| _paletteViewerPage = _designViewer.createPaletteViewerPage(); |
| } |
| return _paletteViewerPage; |
| } |
| |
| /** |
| * @return the edit domain |
| */ |
| public DefaultEditDomain getEditDomain() { |
| if (_editDomain == null) { |
| _editDomain = new DefaultEditDomain(this); |
| |
| // XXX: if i don't do the following line, system will default use |
| // SelectionTool. Don't know where else to set this. Since it is |
| // kind of duplicate |
| // to the DesignerPaletteRoot. |
| _editDomain.setDefaultTool(new RangeSelectionTool()); |
| _editDomain.loadDefaultTool(); |
| |
| // next config the _editDomain |
| // _editDomain.setPaletteRoot(new JSFPaletteRoot()); |
| } |
| return _editDomain; |
| } |
| |
| /** |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.part.MultiPageEditorPart#pageChange(int) |
| */ |
| protected void pageChange(int newPageIndex) { |
| super.pageChange(newPageIndex); |
| |
| deletePreviewFiles(); |
| |
| if (newPageIndex == _previewPageIndex) { |
| // preview page activate, need to regenerate the preview text and |
| // display it. |
| StringBuffer result = new StringBuffer(); |
| try { |
| //Bug 350990 - Web Page Editor intolerably slow |
| if (_mode == MODE_SOURCE) { |
| _designViewer.setModel(getModel()); |
| } |
| DocumentEditPart part = (DocumentEditPart) this._designViewer |
| .getGraphicViewer().getContents(); |
| PreviewHandlerNew.generatePreview(part, result); |
| //Bug 350990 - Web Page Editor intolerably slow |
| if (_mode == MODE_SOURCE) { |
| _designViewer.setModel(null); |
| } |
| } catch (Exception ex) { |
| result = new StringBuffer(); |
| result.append(getModel().getStructuredDocument().getText()); |
| // Error in page changing |
| _log.info("Error.HTMLEditor.6", ex); //$NON-NLS-1$ |
| } |
| File file = PreviewUtil.toFile(result, getEditorInput()); |
| if (file != null) { |
| PREVIEW_FILES_LIST.add(file); |
| getPreviewBrowser().loadFile(file); |
| } else { |
| getPreviewBrowser().getBrowser().setUrl("about:blank"); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /** |
| * @return Returns the _designViewer. |
| */ |
| public IDesignViewer getDesignViewer() { |
| return _designViewer; |
| } |
| |
| /** |
| * @param mode |
| */ |
| public void setDesignerMode(int mode) { |
| boolean modeWasSourceOnly = (_mode == MODE_SOURCE && _mode != mode); |
| if (_sashEditorPart != null && _mode != mode) { |
| switch (mode) { |
| case MODE_SASH_HORIZONTAL: |
| _sashEditorPart.setOrientation(SWT.HORIZONTAL); |
| break; |
| case MODE_DESIGNER: |
| _sashEditorPart.setMaximizedEditor(this._designViewer); |
| break; |
| case MODE_SOURCE: |
| _sashEditorPart.setMaximizedEditor(this._textEditor); |
| //Bug 350990 - Web Page Editor intolerably slow |
| _designViewer.setModel(null); |
| if (_designPageActionContributor != null) { |
| _designPageActionContributor.disableRangeModeActions(); |
| } |
| break; |
| case MODE_SASH_VERTICAL: |
| default: |
| _sashEditorPart.setOrientation(SWT.VERTICAL); |
| } |
| if (getEditorInput() != null) { |
| EditorUtil.setEditorInputDesignModeProperty(getEditorInput(), String.valueOf(mode)); |
| } |
| } |
| this._mode = mode; |
| if (modeWasSourceOnly) { |
| //Bug 350990 - Web Page Editor intolerably slow |
| _designViewer.setModel(getModel()); |
| resynch(); |
| } |
| } |
| |
| /** |
| * Sets the current DesignPageActionContributor instance. |
| * @param designPageActionContributor Current DesignPageActionContributor instance. |
| */ |
| public void setDesignPageActionContributor( |
| final DesignPageActionContributor designPageActionContributor) { |
| _designPageActionContributor = designPageActionContributor; |
| } |
| |
| /* |
| * Set the sash editor mode from the stored file property |
| * or the default preference. |
| */ |
| private void initDesignerMode() { |
| int preferredMode = MODE_SASH_VERTICAL; |
| |
| // If the user has already selected a mode for the file, use it. |
| String prop = null; |
| if (getEditorInput() != null) { |
| prop = EditorUtil.getEditorInputDesignModeProperty(getEditorInput()); |
| } |
| if (prop != null) { |
| try { |
| preferredMode = Integer.parseInt(prop); |
| } catch (NumberFormatException e) { |
| // do nothing; |
| } |
| } else { |
| // Otherwise, get the default mode from preferences. |
| IPreferenceStore pStore = PDPlugin.getDefault().getPreferenceStore(); |
| preferredMode = pStore.getInt(PDPreferences.SASH_EDITOR_MODE_PREF); |
| } |
| |
| setDesignerMode(preferredMode); |
| } |
| |
| /** |
| * @return the current design mode |
| */ |
| public int getDesignerMode() { |
| return this._mode; |
| } |
| |
| private void resynch() { |
| if (_textEditor != null && _designViewer != null) { |
| ISelectionProvider provider = _textEditor.getSelectionProvider(); |
| if (provider != null) { |
| ISelection selection = provider.getSelection(); |
| if (selection instanceof TextSelection) { |
| TextSelection textSelection = (TextSelection)selection; |
| SelectionSynchronizer synchronizer = _designViewer.getSynchronizer(); |
| if (synchronizer != null) { |
| synchronizer.textSelectionChanged( |
| textSelection.getOffset(), |
| textSelection.getOffset() + textSelection.getLength()); |
| } |
| } |
| } |
| } |
| } |
| |
| public IEditorPart getActiveEditor() { |
| IEditorPart result = null; |
| if (_sash) { |
| result = _sashEditorPart.getActiveEditor(); |
| } else { |
| if (_designViewer.getGraphicViewer().getControl().isFocusControl()) { |
| result = _designViewer; |
| } else if (_textEditor.getTextViewer().getControl() |
| .isFocusControl()) { |
| result = _textEditor; |
| } |
| } |
| return result; |
| } |
| |
| public String getPartName() { |
| if (_textEditor != null) { |
| return _textEditor.getPartName(); |
| } |
| return super.getPartName(); |
| } |
| |
| private void deletePreviewFiles() { |
| Iterator itPreviewFiles = PREVIEW_FILES_LIST.iterator(); |
| while (itPreviewFiles.hasNext()) { |
| File file = (File)itPreviewFiles.next(); |
| if (file != null && file.exists()) { |
| file.delete(); |
| } |
| } |
| PREVIEW_FILES_LIST.clear(); |
| } |
| |
| /** |
| * Refreshes the design page. Allows an external action to force a refresh |
| * after an external change, such as a DT skin change. |
| */ |
| public void refreshDesignViewer() { |
| EditPart contentEditPart = _designViewer.getGraphicViewer().getRootEditPart().getContents(); |
| if (contentEditPart instanceof DocumentEditPart) { |
| ((DocumentEditPart)contentEditPart).styleChanged(); |
| } |
| } |
| |
| /** |
| * Adds a {@link IWPEPersistenceListener persistence listener} to this editor. |
| * |
| * <p>This type of listener is cleaned when the editor is disposed.</p> |
| * |
| * @param listener |
| */ |
| public void addPersistenceListener(IWPEPersistenceListener listener) { |
| if (persistenceListeners == null) { |
| persistenceListeners = new ArrayList<IWPEPersistenceListener>(5); |
| } |
| persistenceListeners.add(listener); |
| } |
| |
| /** |
| * Removes a {@link IWPEPersistenceListener persistence listener} added to this |
| * editor. |
| * |
| * <p>This type of listener is cleaned when the editor is disposed.</p> |
| * |
| * @param listener |
| */ |
| public void removePersistenceListener(IWPEPersistenceListener listener) { |
| if (persistenceListeners != null) { |
| if (persistenceListeners.remove(listener) && persistenceListeners.isEmpty()) { |
| persistenceListeners = null; |
| } |
| } |
| } |
| |
| /** |
| * @param type |
| * @return <code>true</code> if operation is to continue, otherwise <code>false</code> |
| */ |
| private boolean firePersistenceEvent(final PersistenceEventType type) { |
| if (persistenceListeners != null) { |
| List<IWPEPersistenceListener> listeners = new ArrayList<IWPEPersistenceListener>(persistenceListeners); |
| IPersistenceEvent event = new IPersistenceEvent() { |
| private boolean cancelled; |
| |
| public HTMLEditor getWPEInstance() { |
| return HTMLEditor.this; |
| } |
| |
| public PersistenceEventType getEventType() { |
| return type; |
| } |
| |
| public boolean isOperationCancelled() { |
| return cancelled; |
| } |
| |
| public void cancelOperation() { |
| cancelled = true; |
| } |
| }; |
| |
| for (IWPEPersistenceListener listener : listeners) { |
| try { |
| listener.notify(event); |
| } catch (Exception e) { |
| PDPlugin.log("Exception thrown while notifying a persistence listener", e); //$NON-NLS-1$ |
| } |
| } |
| return !event.isOperationCancelled(); |
| } |
| return true; |
| } |
| |
| /** |
| * Updates the editor's title image |
| * @param titleImage |
| */ |
| public void updateTitleImage(Image titleImage) { |
| setTitleImage(titleImage); |
| } |
| |
| } |