| /***************************************************************************** |
| * Copyright (c) 2004, 2010 IBM Corporation 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: IBM Corporation - initial API and implementation |
| ****************************************************************************/ |
| package org.eclipse.wst.xml.ui.internal.tabletree; |
| |
| |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExecutableExtension; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.action.IStatusLineManager; |
| import org.eclipse.jface.action.ToolBarManager; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.ITextInputListener; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.viewers.AbstractTreeViewer; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| 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.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.ui.IEditorActionBarContributor; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IPartListener; |
| import org.eclipse.ui.IPartService; |
| import org.eclipse.ui.IPropertyListener; |
| import org.eclipse.ui.IWindowListener; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.ide.IDE; |
| import org.eclipse.ui.ide.IGotoMarker; |
| import org.eclipse.ui.part.MultiPageEditorActionBarContributor; |
| import org.eclipse.ui.part.MultiPageEditorPart; |
| import org.eclipse.ui.part.MultiPageEditorSite; |
| import org.eclipse.ui.part.MultiPageSelectionProvider; |
| import org.eclipse.ui.progress.UIJob; |
| import org.eclipse.wst.sse.core.StructuredModelManager; |
| import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; |
| import org.eclipse.wst.sse.ui.StructuredTextEditor; |
| import org.eclipse.wst.sse.ui.internal.contentoutline.IJFaceNodeAdapter; |
| import org.eclipse.wst.xml.core.internal.provisional.IXMLPreferenceNames; |
| import org.eclipse.wst.xml.core.internal.provisional.contenttype.ContentTypeIdForXML; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| import org.eclipse.wst.xml.ui.internal.Logger; |
| import org.eclipse.wst.xml.ui.internal.XMLUIPlugin; |
| import org.eclipse.wst.xml.ui.internal.contentoutline.JFaceNodeLabelProvider; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| public class XMLMultiPageEditorPart extends MultiPageEditorPart { |
| |
| /** |
| * Internal part activation listener, copied from AbstractTextEditor |
| */ |
| class ActivationListener implements IPartListener, IWindowListener { |
| |
| /** |
| * The maximum number of children the root nodes can have for the |
| * design page to auto expand the root nodes |
| */ |
| private static final int MAX_NUM_CHILD_NODES_FOR_AUTO_EXPAND = 500; |
| |
| /** Cache of the active workbench part. */ |
| private IWorkbenchPart fActivePart; |
| /** Indicates whether activation handling is currently be done. */ |
| private boolean fIsHandlingActivation = false; |
| /** |
| * The part service. |
| * |
| * @since 3.1 |
| */ |
| private IPartService fPartService; |
| |
| /** |
| * Creates this activation listener. |
| * |
| * @param partService |
| * the part service on which to add the part listener |
| * @since 3.1 |
| */ |
| public ActivationListener(IPartService partService) { |
| fPartService = partService; |
| fPartService.addPartListener(this); |
| PlatformUI.getWorkbench().addWindowListener(this); |
| } |
| |
| /** |
| * Disposes this activation listener. |
| * |
| * @since 3.1 |
| */ |
| public void dispose() { |
| fPartService.removePartListener(this); |
| PlatformUI.getWorkbench().removeWindowListener(this); |
| fPartService = null; |
| } |
| |
| /* |
| * @see IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart) |
| */ |
| public void partActivated(IWorkbenchPart part) { |
| fActivePart = part; |
| handleActivation(); |
| } |
| |
| /* |
| * @see IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart) |
| */ |
| public void partBroughtToTop(IWorkbenchPart part) { |
| // do nothing |
| } |
| |
| /* |
| * @see IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart) |
| */ |
| public void partClosed(IWorkbenchPart part) { |
| // do nothing |
| } |
| |
| /* |
| * @see IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart) |
| */ |
| public void partDeactivated(IWorkbenchPart part) { |
| fActivePart = null; |
| } |
| |
| /* |
| * @see IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart) |
| */ |
| public void partOpened(IWorkbenchPart part) { |
| if (fDesignViewer instanceof AbstractTreeViewer) { |
| IStructuredModel model = StructuredModelManager.getModelManager().getExistingModelForRead(getDocument()); |
| try { |
| if (model instanceof IDOMModel) { |
| IDOMDocument modelDocument = ((IDOMModel) model).getDocument(); |
| NodeList rootChildren = modelDocument.getChildNodes(); |
| |
| boolean tooManyChildren = (rootChildren.getLength() > MAX_NUM_CHILD_NODES_FOR_AUTO_EXPAND); |
| /* |
| * For each root (there should really only be one |
| * real root but there are also could be empty |
| * text regions and doc type at the root level) |
| * determine if it has to many children or not to |
| * auto expand |
| */ |
| for (int i = 0; i < rootChildren.getLength() && !tooManyChildren; ++i) { |
| tooManyChildren = (rootChildren.item(i).getChildNodes().getLength() > MAX_NUM_CHILD_NODES_FOR_AUTO_EXPAND); |
| } |
| |
| /* |
| * if root node does not have to many children then |
| * auto expand the root node |
| */ |
| if (!tooManyChildren) { |
| ((AbstractTreeViewer) fDesignViewer).expandToLevel(2); |
| } |
| } |
| } |
| finally { |
| if (model != null) { |
| model.releaseFromRead(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Handles the activation triggering a element state check in the |
| * editor. |
| */ |
| void handleActivation() { |
| if (fIsHandlingActivation || (getTextEditor() == null)) { |
| return; |
| } |
| |
| if (fActivePart == XMLMultiPageEditorPart.this) { |
| fIsHandlingActivation = true; |
| try { |
| getTextEditor().safelySanityCheckState(getEditorInput()); |
| } |
| finally { |
| fIsHandlingActivation = false; |
| } |
| } |
| } |
| |
| /* |
| * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow) |
| * @since 3.1 |
| */ |
| public void windowActivated(IWorkbenchWindow window) { |
| if (window == getEditorSite().getWorkbenchWindow()) { |
| /* |
| * Workaround for problem described in |
| * http://dev.eclipse.org/bugs/show_bug.cgi?id=11731 Will be |
| * removed when SWT has solved the problem. |
| */ |
| window.getShell().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| handleActivation(); |
| } |
| }); |
| } |
| } |
| |
| /* |
| * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow) |
| * @since 3.1 |
| */ |
| public void windowDeactivated(IWorkbenchWindow window) { |
| // do nothing |
| } |
| |
| /* |
| * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow) |
| * @since 3.1 |
| */ |
| public void windowClosed(IWorkbenchWindow window) { |
| // do nothing |
| } |
| |
| /* |
| * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow) |
| * @since 3.1 |
| */ |
| public void windowOpened(IWorkbenchWindow window) { |
| // do nothing |
| } |
| } |
| |
| /** |
| * Listens for selection from the source page, applying it to the design |
| * viewer. |
| */ |
| private class TextEditorPostSelectionAdapter extends UIJob implements ISelectionChangedListener { |
| boolean forcePostSelection = false; |
| ISelection selection = null; |
| |
| public TextEditorPostSelectionAdapter() { |
| super(getTitle()); |
| setUser(true); |
| } |
| |
| public IStatus runInUIThread(IProgressMonitor monitor) { |
| if (selection != null) { |
| fDesignViewer.getSelectionProvider().setSelection(selection); |
| } |
| return Status.OK_STATUS; |
| } |
| |
| public void selectionChanged(SelectionChangedEvent event) { |
| if ((fDesignViewer != null) && ((getActivePage() != fDesignPageIndex) || !XMLMultiPageEditorPart.this.equals(getSite().getPage().getActivePart()))) { |
| if (forcePostSelection) { |
| selection = event.getSelection(); |
| schedule(200); |
| } |
| else { |
| fDesignViewer.getSelectionProvider().setSelection(event.getSelection()); |
| } |
| } |
| } |
| } |
| |
| private class PageInitializationData { |
| IConfigurationElement fElement; |
| String fPropertyName; |
| Object fData; |
| |
| PageInitializationData(IConfigurationElement cfig, String propertyName, Object data) { |
| super(); |
| fElement = cfig; |
| fPropertyName = propertyName; |
| fData = data; |
| } |
| |
| void sendInitializationData(IExecutableExtension executableExtension) { |
| try { |
| executableExtension.setInitializationData(fElement, fPropertyName, fData); |
| } |
| catch (CoreException e) { |
| Logger.logException(e); |
| } |
| } |
| } |
| |
| /** |
| * Internal IPropertyListener on the source page |
| */ |
| class PropertyListener implements IPropertyListener { |
| public void propertyChanged(Object source, int propId) { |
| switch (propId) { |
| // had to implement input changed "listener" so that |
| // StructuredTextEditor could tell it containing editor that |
| // the input has change, when a 'resource moved' event is |
| // found. |
| case IEditorPart.PROP_INPUT : { |
| if (source == getTextEditor() && fDesignViewer instanceof XMLTableTreeViewer) { |
| IStructuredModel model = StructuredModelManager.getModelManager().getExistingModelForRead(getDocument()); |
| try { |
| if (model instanceof IDOMModel) { |
| IDOMDocument modelDocument = ((IDOMModel) model).getDocument(); |
| Object designInput = ((XMLTableTreeViewer) fDesignViewer).getInput(); |
| if (modelDocument != designInput) |
| setInput(getTextEditor().getEditorInput()); |
| } |
| } |
| finally { |
| if (model != null) |
| model.releaseFromRead(); |
| } |
| } |
| } |
| case IEditorPart.PROP_DIRTY : { |
| if (source == getTextEditor()) { |
| if (getTextEditor().getEditorInput() != getEditorInput()) { |
| setInput(getTextEditor().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. |
| */ |
| ((Control) getTextEditor().getAdapter(Control.class)).getDisplay().asyncExec(runnable); |
| } |
| } |
| break; |
| } |
| case IWorkbenchPart.PROP_TITLE : { |
| // update the input if the title is changed |
| if (source == getTextEditor()) { |
| if (getTextEditor().getEditorInput() != getEditorInput()) { |
| setInput(getTextEditor().getEditorInput()); |
| } |
| } |
| break; |
| } |
| default : { |
| // propagate changes. Is this needed? Answer: Yes. |
| if (source == getTextEditor()) { |
| _firePropertyChange(propId); |
| } |
| break; |
| } |
| } |
| |
| } |
| } |
| |
| class TextInputListener implements ITextInputListener { |
| public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { |
| // do nothing |
| } |
| |
| public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { |
| if ((fDesignViewer != null) && (newInput != null)) { |
| fDesignViewer.setDocument(newInput); |
| } |
| } |
| } |
| |
| class StatusLineLabelProvider extends JFaceNodeLabelProvider { |
| public StatusLineLabelProvider() { |
| } |
| |
| public String getText(Object element) { |
| if (element == null) |
| return null; |
| |
| Node node = (Node) element; |
| if (node.getNodeType() == Node.ATTRIBUTE_NODE) { |
| return getText(((Attr) node).getOwnerElement()); |
| } |
| |
| StringBuffer s = new StringBuffer(); |
| if (node.getNodeType() != Node.DOCUMENT_NODE) { |
| while (node != null && node instanceof INodeNotifier) { |
| INodeNotifier notifier = (INodeNotifier) node; |
| if (node.getNodeType() != Node.DOCUMENT_NODE) { |
| IJFaceNodeAdapter adapter = (IJFaceNodeAdapter) notifier.getAdapterFor(IJFaceNodeAdapter.class); |
| if (adapter != null) { |
| s.insert(0, adapter.getLabelText(node)); |
| } |
| } |
| node = node.getParentNode(); |
| if (node != null && node.getNodeType() != Node.DOCUMENT_NODE) |
| s.insert(0, IPath.SEPARATOR); |
| } |
| } |
| return s.toString(); |
| } |
| |
| public Image getImage(Object element) { |
| if (element == null) |
| return null; |
| |
| Node node = (Node) element; |
| if (node.getNodeType() == Node.ATTRIBUTE_NODE) { |
| return getImage(((Attr) node).getOwnerElement()); |
| } |
| return super.getImage(element); |
| } |
| } |
| |
| /** The design page index. */ |
| private int fDesignPageIndex; |
| |
| /** The design viewer */ |
| IDesignViewer fDesignViewer; |
| /** Any container for the design viewer */ |
| private Composite fDesignContainer; |
| |
| private ActivationListener fActivationListener; |
| |
| IPropertyListener fPropertyListener = null; |
| |
| /** The source page index. */ |
| int fSourcePageIndex; |
| |
| /** The text editor. */ |
| private StructuredTextEditor fTextEditor; |
| |
| private TextEditorPostSelectionAdapter fTextEditorSelectionListener; |
| |
| private ILabelProvider fStatusLineLabelProvider; |
| |
| private PageInitializationData fPageInitializer; |
| |
| private ToolBarManager fToolbarManager; |
| |
| private boolean fAllocateToolbar = true; |
| |
| /** |
| * StructuredTextMultiPageEditorPart constructor comment. |
| */ |
| public XMLMultiPageEditorPart() { |
| super(); |
| fStatusLineLabelProvider = new StatusLineLabelProvider(); |
| } |
| |
| /* |
| * This method is just to make firePropertyChanged accessible from some |
| * (anonomous) inner classes. |
| */ |
| void _firePropertyChange(int property) { |
| super.firePropertyChange(property); |
| } |
| |
| /** |
| * Adds the source page of the multi-page editor. |
| */ |
| private void addSourcePage() throws PartInitException { |
| fSourcePageIndex = addPage(fTextEditor, getEditorInput()); |
| setPageText(fSourcePageIndex, XMLEditorMessages.XMLMultiPageEditorPart_0); |
| |
| firePropertyChange(PROP_TITLE); |
| |
| // Changes to the Text Viewer's document instance should also |
| // force an |
| // input refresh |
| fTextEditor.getTextViewer().addTextInputListener(new TextInputListener()); |
| } |
| |
| /** |
| * 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. |
| */ |
| private void connectDesignPage() { |
| if (fDesignViewer != null) { |
| fDesignViewer.setDocument(getDocument()); |
| } |
| |
| /* |
| * Connect selection from the Design page to the selection provider |
| * for the XMLMultiPageEditorPart so that selection changes in the |
| * Design page will propagate across the workbench |
| */ |
| if (fDesignViewer.getSelectionProvider() instanceof IPostSelectionProvider) { |
| ((IPostSelectionProvider) fDesignViewer.getSelectionProvider()).addPostSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| ((MultiPageSelectionProvider) getSite().getSelectionProvider()).firePostSelectionChanged(event); |
| } |
| }); |
| } |
| fDesignViewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| ((MultiPageSelectionProvider) getSite().getSelectionProvider()).fireSelectionChanged(event); |
| } |
| }); |
| |
| /* |
| * Connect selection from the Design page to the selection provider of |
| * the Source page so that selection in the Design page will drive |
| * selection in the Source page. Prefer post selection. |
| */ |
| if (fDesignViewer.getSelectionProvider() instanceof IPostSelectionProvider) { |
| ((IPostSelectionProvider) fDesignViewer.getSelectionProvider()).addPostSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged(final SelectionChangedEvent event) { |
| /* |
| * Only force selection update if source page is not |
| * active |
| */ |
| if (getActivePage() != fSourcePageIndex) { |
| getTextEditor().getSelectionProvider().setSelection(event.getSelection()); |
| } |
| } |
| }); |
| } |
| else { |
| fDesignViewer.getSelectionProvider().addSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| /* |
| * Only force selection update if source page is not |
| * active |
| */ |
| if (getActivePage() != fSourcePageIndex) { |
| getTextEditor().getSelectionProvider().setSelection(event.getSelection()); |
| } |
| /* |
| * Bug 210481 - Removed call to updateStatusLine because |
| * this is already handled by the selection in the source |
| * page |
| */ |
| } |
| }); |
| } |
| |
| /* |
| * Handle double-click in the Design page by selecting the |
| * corresponding amount of text in the Source page. |
| * |
| * Warning: This implies more knowledge of the design viewer's underlying |
| * Control than expressed in the IDesignViewer interface |
| */ |
| fDesignViewer.getControl().addListener(SWT.MouseDoubleClick, new Listener() { |
| public void handleEvent(Event event) { |
| ISelection selection = fDesignViewer.getSelectionProvider().getSelection(); |
| int start = -1; |
| int length = -1; |
| if (selection instanceof IStructuredSelection) { |
| /* |
| * selection goes from the start of the first object to |
| * the end of the last |
| */ |
| IStructuredSelection structuredSelection = (IStructuredSelection) selection; |
| Object o = structuredSelection.getFirstElement(); |
| Object o2 = null; |
| if (structuredSelection.size() > 1) { |
| o2 = structuredSelection.toArray()[structuredSelection.size() - 1]; |
| } |
| else { |
| o2 = o; |
| } |
| if (o instanceof IndexedRegion) { |
| start = ((IndexedRegion) o).getStartOffset(); |
| length = ((IndexedRegion) o2).getEndOffset() - start; |
| } |
| else if (o2 instanceof ITextRegion) { |
| start = ((ITextRegion) o).getStart(); |
| length = ((ITextRegion) o2).getEnd() - start; |
| } |
| } |
| else if (selection instanceof ITextSelection) { |
| start = ((ITextSelection) selection).getOffset(); |
| length = ((ITextSelection) selection).getLength(); |
| } |
| if ((start > -1) && (length > -1)) { |
| getTextEditor().selectAndReveal(start, length); |
| } |
| } |
| }); |
| |
| /* |
| * Connect selection from the Source page to the selection provider of |
| * the Design page so that selection in the Source page will drive |
| * selection in the Design page. Prefer post selection. |
| */ |
| ISelectionProvider provider = getTextEditor().getSelectionProvider(); |
| if (fTextEditorSelectionListener == null) { |
| fTextEditorSelectionListener = new TextEditorPostSelectionAdapter(); |
| } |
| if (provider instanceof IPostSelectionProvider) { |
| fTextEditorSelectionListener.forcePostSelection = false; |
| ((IPostSelectionProvider) provider).addPostSelectionChangedListener(fTextEditorSelectionListener); |
| } |
| else { |
| fTextEditorSelectionListener.forcePostSelection = true; |
| provider.addSelectionChangedListener(fTextEditorSelectionListener); |
| } |
| } |
| |
| /** |
| * Create and Add the Design Page using a registered factory |
| * |
| */ |
| private void createAndAddDesignPage() { |
| IDesignViewer designViewer = createDesignPage(); |
| |
| fDesignViewer = designViewer; |
| // 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 |
| if (fDesignContainer != null) |
| fDesignPageIndex = addPage(fDesignContainer); |
| else |
| fDesignPageIndex = addPage(designViewer.getControl()); |
| |
| setPageText(fDesignPageIndex, designViewer.getTitle()); |
| } |
| |
| protected IDesignViewer createDesignPage() { |
| Composite container = getDesignContainer(getContainer()); |
| |
| XMLTableTreeViewer tableTreeViewer = new XMLTableTreeViewer(container); |
| tableTreeViewer.getControl().setLayoutData(new GridData(GridData.FILL_BOTH)); |
| // Set the default info-pop for XML design viewer. |
| XMLUIPlugin.getInstance().getWorkbench().getHelpSystem().setHelp(tableTreeViewer.getControl(), XMLTableTreeHelpContextIds.XML_DESIGN_VIEW_HELPID); |
| |
| // Toolbar wasn't allocated |
| if (fToolbarManager != null) { |
| addToolBarActions(tableTreeViewer); |
| } |
| return tableTreeViewer; |
| } |
| |
| protected Composite getDesignContainer(Composite defaultContainer) { |
| Composite container = defaultContainer; |
| // create a container to hold the toolbar if it should be created |
| if (fAllocateToolbar) { |
| container = new Composite(defaultContainer, SWT.NONE); |
| GridLayout layout = new GridLayout(); |
| layout.marginHeight = 0; |
| layout.verticalSpacing = 0; |
| layout.marginWidth = 0; |
| container.setLayout(layout); |
| |
| ToolBar tb = new ToolBar(container, SWT.FLAT); |
| fToolbarManager = new ToolBarManager(tb); |
| tb.setLayoutData(new GridData(GridData.END, GridData.VERTICAL_ALIGN_BEGINNING, true, false)); |
| fDesignContainer = container; |
| } |
| return container; |
| } |
| |
| private void addToolBarActions(IDesignViewer viewer) { |
| if (viewer instanceof AbstractTreeViewer) { |
| ViewerExpandCollapseAction expand = new ViewerExpandCollapseAction(true); |
| ViewerExpandCollapseAction collapse = new ViewerExpandCollapseAction(false); |
| fToolbarManager.add(expand); |
| fToolbarManager.add(collapse); |
| fToolbarManager.update(true); |
| |
| |
| expand.setViewer((AbstractTreeViewer) viewer); |
| collapse.setViewer((AbstractTreeViewer) viewer); |
| } |
| } |
| |
| /** |
| * 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 |
| createSourcePage(); |
| |
| createAndAddDesignPage(); |
| addSourcePage(); |
| connectDesignPage(); |
| |
| // set the active editor in the action bar contributor first |
| // before setactivepage calls action bar contributor's |
| // setactivepage (bug141013 - remove when bug151488 is fixed) |
| IEditorActionBarContributor contributor = getEditorSite().getActionBarContributor(); |
| if (contributor instanceof MultiPageEditorActionBarContributor) { |
| ((MultiPageEditorActionBarContributor) contributor).setActiveEditor(this); |
| } |
| |
| int activePageIndex = getPreferenceStore().getInt(IXMLPreferenceNames.LAST_ACTIVE_PAGE); |
| if ((activePageIndex >= 0) && (activePageIndex < getPageCount())) { |
| setActivePage(activePageIndex); |
| } |
| else { |
| setActivePage(fSourcePageIndex); |
| } |
| } |
| catch (PartInitException e) { |
| Logger.logException(e); |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * @see org.eclipse.ui.part.MultiPageEditorPart#createSite(org.eclipse.ui.IEditorPart) |
| */ |
| protected IEditorSite createSite(IEditorPart editor) { |
| IEditorSite site = null; |
| if (editor == fTextEditor) { |
| site = new MultiPageEditorSite(this, editor) { |
| /** |
| * @see org.eclipse.ui.part.MultiPageEditorSite#getActionBarContributor() |
| */ |
| public IEditorActionBarContributor getActionBarContributor() { |
| IEditorActionBarContributor contributor = super.getActionBarContributor(); |
| IEditorActionBarContributor multiContributor = XMLMultiPageEditorPart.this.getEditorSite().getActionBarContributor(); |
| if (multiContributor instanceof XMLMultiPageEditorActionBarContributor) { |
| contributor = ((XMLMultiPageEditorActionBarContributor) multiContributor).sourceViewerActionContributor; |
| } |
| return contributor; |
| } |
| |
| public String getId() { |
| // sets this id so nested editor is considered xml source |
| // page |
| return ContentTypeIdForXML.ContentTypeID_XML + ".source"; //$NON-NLS-1$; |
| } |
| }; |
| } |
| else { |
| site = super.createSite(editor); |
| } |
| return site; |
| } |
| |
| /** |
| * Creates the source page of the multi-page editor. |
| */ |
| protected void createSourcePage() throws PartInitException { |
| fTextEditor = createTextEditor(); |
| fTextEditor.setEditorPart(this); |
| |
| /* |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=134301 - XML editor |
| * does not remember font settings |
| * |
| * @see |
| * org.eclipse.ui.texteditor.AbstractTextEditor#getSymbolicFontName() |
| */ |
| fPageInitializer.sendInitializationData(fTextEditor); |
| |
| if (fPropertyListener == null) { |
| fPropertyListener = new PropertyListener(); |
| } |
| fTextEditor.addPropertyListener(fPropertyListener); |
| } |
| |
| /** |
| * Method createTextEditor. |
| * |
| * @return StructuredTextEditor |
| */ |
| private StructuredTextEditor createTextEditor() { |
| return new StructuredTextEditor(); |
| } |
| |
| private void disconnectDesignPage() { |
| if (fDesignViewer != null) { |
| fDesignViewer.setDocument(null); |
| } |
| } |
| |
| public void dispose() { |
| Logger.trace("Source Editor", "XMLMultiPageEditorPart::dispose entry"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| disconnectDesignPage(); |
| |
| if (fActivationListener != null) { |
| fActivationListener.dispose(); |
| fActivationListener = null; |
| } |
| |
| if ((fTextEditor != null) && (fPropertyListener != null)) { |
| fTextEditor.removePropertyListener(fPropertyListener); |
| } |
| |
| // moved to last when added window ... seems like |
| // we'd be in danger of losing some data, like site, |
| // or something. |
| super.dispose(); |
| |
| Logger.trace("Source Editor", "StructuredTextMultiPageEditorPart::dispose exit"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| public void doSave(IProgressMonitor monitor) { |
| fTextEditor.doSave(monitor); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.ISaveablePart#doSaveAs() |
| */ |
| public void doSaveAs() { |
| fTextEditor.doSaveAs(); |
| /* |
| * Update the design viewer since the editor input would have changed |
| * to the new file. |
| */ |
| if (fDesignViewer != null) { |
| fDesignViewer.setDocument(getDocument()); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) |
| */ |
| public Object getAdapter(Class key) { |
| Object result = null; |
| |
| // we extend superclass, not override it, so allow it first |
| // chance to satisfy request. |
| result = super.getAdapter(key); |
| |
| if (result == null) { |
| if (key == IDesignViewer.class) { |
| result = fDesignViewer; |
| |
| } |
| else if (key.equals(IGotoMarker.class)) { |
| result = new IGotoMarker() { |
| public void gotoMarker(IMarker marker) { |
| XMLMultiPageEditorPart.this.gotoMarker(marker); |
| } |
| }; |
| } |
| else { |
| /* |
| * DMW: I'm bullet-proofing this because its been reported (on |
| * very early versions) a null pointer sometimes happens here |
| * on startup, when an editor has been left open when |
| * workbench shutdown. |
| */ |
| if (fTextEditor != null) { |
| result = fTextEditor.getAdapter(key); |
| } |
| } |
| } |
| return result; |
| } |
| |
| private IDocument getDocument() { |
| IDocument document = null; |
| if (fTextEditor != null) { |
| document = fTextEditor.getDocumentProvider().getDocument(fTextEditor.getEditorInput()); |
| } |
| return document; |
| } |
| |
| private IPreferenceStore getPreferenceStore() { |
| return XMLUIPlugin.getDefault().getPreferenceStore(); |
| } |
| |
| StructuredTextEditor getTextEditor() { |
| return fTextEditor; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.IWorkbenchPart#getTitle() |
| */ |
| 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; |
| } |
| |
| void gotoMarker(IMarker marker) { |
| setActivePage(fSourcePageIndex); |
| IDE.gotoMarker(fTextEditor, marker); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.IEditorPart#init(org.eclipse.ui.IEditorSite, |
| * org.eclipse.ui.IEditorInput) |
| */ |
| public void init(IEditorSite site, IEditorInput input) throws PartInitException { |
| try { |
| super.init(site, input); |
| // we want to listen for our own activation |
| fActivationListener = new ActivationListener(site.getWorkbenchWindow().getPartService()); |
| } |
| catch (Exception e) { |
| Logger.logException("exception initializing " + getClass().getName(), e); //$NON-NLS-1$ |
| } |
| setPartName(input.getName()); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed() |
| */ |
| public boolean isSaveAsAllowed() { |
| return (fTextEditor != null) && fTextEditor.isSaveAsAllowed(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.ISaveablePart#isSaveOnCloseNeeded() |
| */ |
| public boolean isSaveOnCloseNeeded() { |
| // overriding super class since it does a lowly isDirty! |
| if (fTextEditor != null) { |
| return fTextEditor.isSaveOnCloseNeeded(); |
| } |
| return isDirty(); |
| } |
| |
| /** |
| * Prevents the creation of the in-editor toolbar, if called before |
| * createPageContainer() during editor initialization. |
| */ |
| protected final void noToolbar() { |
| fAllocateToolbar = false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.ui.part.MultiPageEditorPart#pageChange(int) |
| */ |
| protected void pageChange(int newPageIndex) { |
| super.pageChange(newPageIndex); |
| saveLastActivePageIndex(newPageIndex); |
| |
| if (newPageIndex == fDesignPageIndex) { |
| // design page isn't an IEditorPart, therefore we have to send |
| // selection changes ourselves |
| ISelectionProvider selectionProvider = fDesignViewer.getSelectionProvider(); |
| if (selectionProvider != null) { |
| SelectionChangedEvent event = new SelectionChangedEvent(selectionProvider, selectionProvider.getSelection()); |
| ((MultiPageSelectionProvider) getSite().getSelectionProvider()).fireSelectionChanged(event); |
| ((MultiPageSelectionProvider) getSite().getSelectionProvider()).firePostSelectionChanged(event); |
| } |
| } |
| } |
| |
| private void saveLastActivePageIndex(int newPageIndex) { |
| // save the last active page index to preference manager |
| getPreferenceStore().setValue(IXMLPreferenceNames.LAST_ACTIVE_PAGE, newPageIndex); |
| } |
| |
| public void setFocus() { |
| super.setFocus(); |
| Control control = fDesignViewer.getControl(); |
| control.setFocus(); |
| |
| if (control instanceof Tree) { |
| if (((Tree) control).getItemCount() > 0) { |
| TreeItem item = ((Tree) control).getItem(0); |
| ((Tree) control).setSelection(item); |
| } |
| } |
| // 271382 - Focus not set properly after activating XML editor |
| control.forceFocus(); |
| } |
| |
| public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) { |
| super.setInitializationData(cfig, propertyName, data); |
| fPageInitializer = new PageInitializationData(cfig, propertyName, data); |
| } |
| |
| /* |
| * (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 (or even exist for) the input just yet. Later notification |
| * from the TextViewer could set us straight, although it's not |
| * guaranteed to happen after the model has been created. |
| */ |
| super.setInput(input); |
| if (fDesignViewer != null) { |
| fDesignViewer.setDocument(getDocument()); |
| } |
| setPartName(input.getName()); |
| } |
| |
| void updateStatusLine(ISelection selection) { |
| IStatusLineManager statusLineManager = getEditorSite().getActionBars().getStatusLineManager(); |
| if (fStatusLineLabelProvider != null && statusLineManager != null) { |
| String text = null; |
| Image image = null; |
| if (selection instanceof IStructuredSelection && !selection.isEmpty()) { |
| Object firstElement = ((IStructuredSelection) selection).getFirstElement(); |
| if (firstElement != null) { |
| text = fStatusLineLabelProvider.getText(firstElement); |
| image = fStatusLineLabelProvider.getImage((firstElement)); |
| } |
| } |
| if (image == null) { |
| statusLineManager.setMessage(text); |
| } |
| else { |
| statusLineManager.setMessage(image, text); |
| } |
| } |
| } |
| } |