/***************************************************************************** * Copyright (c) 2011, 2014 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.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.DefaultPositionUpdater; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IPositionUpdater; import org.eclipse.jface.text.Position; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IMemento; import org.eclipse.ui.INavigationLocation; import org.eclipse.ui.NavigationLocation; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditor; import org.eclipse.ui.texteditor.TextSelectionNavigationLocation; import org.eclipse.wst.sse.core.StructuredModelManager; 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.IStructuredDocument; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; /** * {@link NavigationLocation} that is loosely based on {@link TextSelectionNavigation} * but operates on the {@link XMLMultiPageEditorPart}'s design page. History is * preserved as positions, but the selection set on the viewer is the indexed region * */ class DesignPageNavigationLocation extends NavigationLocation { // Memento tags and values private static final String TAG_X = "x"; //$NON-NLS-1$ private static final String TAG_Y = "y"; //$NON-NLS-1$ private static final String TAG_INFO = "info"; //$NON-NLS-1$ private static final String INFO_DELETED = "deleted"; //$NON-NLS-1$ private static final String INFO_NOT_DELETED = "not_deleted"; //$NON-NLS-1$ private static final String CATEGORY = "__navigation_" + TextSelectionNavigationLocation.class.hashCode(); //$NON-NLS-1$ private static final IPositionUpdater fgPositionUpdater = new DefaultPositionUpdater(CATEGORY); private Position fPosition; private IDocument fDocument; private Position fSavedPosition; private IDesignViewer fViewer; DesignPageNavigationLocation(IEditorPart part, IDesignViewer viewer, boolean initialize) { super(part); fViewer = viewer; if (initialize) { final ISelection selection = fViewer.getSelectionProvider().getSelection(); IEditorPart textPart = getTextEditorPart(); if (textPart != null) { IDocument document = getDocument((ITextEditor) textPart); if (selection instanceof IStructuredSelection && !selection.isEmpty()) { Object first = ((IStructuredSelection) selection).getFirstElement(); if (first instanceof IDOMNode) { IDOMNode node = (IDOMNode) first; Position position= new Position(node.getStartOffset(), node.getLength()); if (installOnDocument(document, position)) { fDocument = document; fPosition = position; if (!part.isDirty()) fSavedPosition = new Position(fPosition.offset, fPosition.length); } } } else { // The editor may not necessarily have a selection when opened Position position= new Position(0, 0); if (installOnDocument(document, position)) { fDocument = document; fPosition = position; if (!part.isDirty()) fSavedPosition = new Position(fPosition.offset, fPosition.length); } } } } } /** * Returns the {@link ITextEditor} associated with this editor part * @return {@link IEditorPart} that is o */ protected ITextEditor getTextEditorPart() { IEditorPart part = super.getEditorPart(); if (part != null) { return (ITextEditor) part.getAdapter(ITextEditor.class); } return null; } /** * Returns the text editor's document. * * @param part * the text editor * @return the document of the given text editor */ private IDocument getDocument(ITextEditor part) { IDocumentProvider provider = part.getDocumentProvider(); return provider.getDocument(part.getEditorInput()); } /** * Installs the given position on the given document. * * @param document * the document * @param position * the position * @return <code>true</code> if the position could be installed */ private boolean installOnDocument(IDocument document, Position position) { if (document != null && position != null) { if (!document.containsPositionCategory(CATEGORY)) { document.addPositionCategory(CATEGORY); document.addPositionUpdater(fgPositionUpdater); } try { document.addPosition(CATEGORY, position); return true; } catch (BadLocationException e) { } catch (BadPositionCategoryException e) { } } return false; } /** * Uninstalls the given position from the given document. * * @param document * the document * @param position * the position * @return <code>true</code> if the position could be uninstalled */ private boolean uninstallFromDocument(IDocument document, Position position) { if (document != null && position != null) { try { document.removePosition(CATEGORY, position); Position[] category = document.getPositions(CATEGORY); if (category == null || category.length == 0) { document.removePositionCategory(CATEGORY); document.removePositionUpdater(fgPositionUpdater); } return true; } catch (BadPositionCategoryException e) { } } return false; } /* * @see Object#toString() */ public String toString() { return "Selection<" + fPosition + ">"; //$NON-NLS-1$ //$NON-NLS-2$ } /** * Tells whether this location is equal to the current location in the given * text editor. * * @param part * the text editor * @return <code>true</code> if the locations are equal */ private boolean equalsLocationOf() { if (fPosition == null) return true; if (fPosition.isDeleted) return false; final ISelection selection = fViewer.getSelectionProvider().getSelection(); if (selection instanceof IStructuredSelection) { final Object first = ((IStructuredSelection) selection).getFirstElement(); if (first instanceof IDOMNode) { final IDOMNode node = (IDOMNode) first; return fPosition.offset == node.getStartOffset() && fPosition.length == node.getLength(); } } return false; } public void dispose() { uninstallFromDocument(fDocument, fPosition); fDocument = null; fPosition = null; fSavedPosition = null; super.dispose(); } /** * Releases the state of this location. */ public void releaseState() { // deactivate uninstallFromDocument(fDocument, fPosition); fDocument = null; fPosition = null; fSavedPosition = null; super.releaseState(); } /** * Merges the given location into this one. * * @param location * the location to merge into this one * @return <code>true<code> if merging was successful */ public boolean mergeInto(INavigationLocation location) { if (location == null) return false; if (getClass() != location.getClass()) return false; if (fPosition == null || fPosition.isDeleted) return true; DesignPageNavigationLocation s = (DesignPageNavigationLocation) location; if (s.fPosition == null || s.fPosition.isDeleted) { uninstallFromDocument(fDocument, fPosition); s.fDocument = fDocument; s.fPosition = fPosition; s.fSavedPosition = fSavedPosition; return true; } if (s.fDocument == fDocument) { if (s.fPosition.overlapsWith(fPosition.offset, fPosition.length) || fPosition.offset + fPosition.length == s.fPosition.offset || s.fPosition.offset + s.fPosition.length == fPosition.offset) { s.fPosition.offset = fPosition.offset; s.fPosition.length = fPosition.length; return true; } } return false; } /** * Restores this location. */ public void restoreLocation() { if (fPosition == null || fPosition.isDeleted) return; if (fViewer instanceof Viewer) { ((Viewer) fViewer).setSelection(getSelection(), true); } } private ISelection getSelection() { ISelection selection = null; IStructuredModel model = null; final ITextEditor editor = getTextEditorPart(); if (editor != null) { try { final IDocument document = getDocument(editor); if (document instanceof IStructuredDocument) { model = StructuredModelManager.getModelManager().getModelForRead((IStructuredDocument) document); if (model != null) { final IndexedRegion region = model.getIndexedRegion(fPosition.offset); if (region != null) { selection = new StructuredSelection(region); } } } } finally { if (model != null) { model.releaseFromRead(); } } } return selection; } /** * Restores the object state from the given memento. * * @param memento * the memento */ public void restoreState(IMemento memento) { final ITextEditor part = getTextEditorPart(); if (part != null) { // restore fDocument = getDocument(part); Integer offset = memento.getInteger(TAG_X); Integer length = memento.getInteger(TAG_Y); String deleted = memento.getString(TAG_INFO); if (offset != null && length != null) { Position p = new Position(offset.intValue(), length.intValue()); if (deleted != null) p.isDeleted = INFO_DELETED.equals(deleted) ? true : false; // activate if (installOnDocument(fDocument, p)) { fPosition = p; if (!part.isDirty()) fSavedPosition = new Position(fPosition.offset, fPosition.length); } } } } /** * Stores the object state into the given memento. * * @param memento * the memento */ public void saveState(IMemento memento) { if (fSavedPosition != null) { memento.putInteger(TAG_X, fSavedPosition.offset); memento.putInteger(TAG_Y, fSavedPosition.length); memento.putString(TAG_INFO, (fSavedPosition.isDeleted ? INFO_DELETED : INFO_NOT_DELETED)); } } /** * Hook method which is called when the given editor has been saved. * * @param part * the editor part */ public void partSaved(IEditorPart part) { // http://dev.eclipse.org/bugs/show_bug.cgi?id=25440 if (fPosition == null || fPosition.isDeleted()) fSavedPosition = null; else fSavedPosition = new Position(fPosition.offset, fPosition.length); } /** * Updates the this location. */ public void update() { final IEditorPart part = getEditorPart(); if (part == null || equalsLocationOf()) return; final ISelection selection = fViewer.getSelectionProvider().getSelection(); if (selection == null || selection.isEmpty()) return; if (selection instanceof IStructuredSelection) { Object first = ((IStructuredSelection) selection).getFirstElement(); if (first instanceof IDOMNode) { IDOMNode node = (IDOMNode) first; fPosition.offset = node.getStartOffset(); fPosition.length = node.getLength(); fPosition.isDeleted = false; if (!part.isDirty()) fSavedPosition = new Position(fPosition.offset, fPosition.length); } } } }