diff options
11 files changed, 565 insertions, 68 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index e5e8229..f7fd525 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -622,7 +622,10 @@ <quickOutlineConfiguration class="com.android.ide.eclipse.adt.internal.editors.AndroidQuickOutlineConfiguration" - target="com.android.ide.eclipse.adt.internal.editors.CommonXmlEditor" /> + target="org.eclipse.core.runtime.xml" /> + <contentOutlineConfiguration + class="com.android.ide.eclipse.adt.internal.editors.AndroidOutlineConfiguration" + target="org.eclipse.core.runtime.xml" /> <provisionalConfiguration type="org.eclipse.jface.text.quickassist.IQuickAssistProcessor" class="com.android.ide.eclipse.adt.internal.editors.formatting.XmlQuickAssistManager" diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidOutlineConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidOutlineConfiguration.java new file mode 100644 index 0000000..6061d92 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidOutlineConfiguration.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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 com.android.ide.eclipse.adt.internal.editors; + + +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.wst.xml.ui.views.contentoutline.XMLContentOutlineConfiguration; + +/** + * Custom version of {@link XMLContentOutlineConfiguration} which adds in icons and + * details such as id or name, to the labels. + */ +public class AndroidOutlineConfiguration extends XMLContentOutlineConfiguration { + /** Constructs a new {@link AndroidOutlineConfiguration} */ + public AndroidOutlineConfiguration() { + } + + @Override + public ILabelProvider getLabelProvider(TreeViewer viewer) { + return new OutlineLabelProvider(); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidQuickOutlineConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidQuickOutlineConfiguration.java index 7b8b2ac..0a8e9dc 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidQuickOutlineConfiguration.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidQuickOutlineConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,61 +16,20 @@ package com.android.ide.eclipse.adt.internal.editors; -import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI; -import static com.android.ide.common.layout.LayoutConstants.ATTR_ID; -import static com.android.ide.common.layout.LayoutConstants.ATTR_NAME; - import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.swt.graphics.Image; -import org.eclipse.wst.xml.ui.internal.contentoutline.JFaceNodeLabelProvider; import org.eclipse.wst.xml.ui.internal.quickoutline.XMLQuickOutlineConfiguration; -import org.w3c.dom.Element; /** * Custom version of {@link XMLQuickOutlineConfiguration} which adds in icons and * details such as id or name, to the labels. */ -@SuppressWarnings("restriction") public class AndroidQuickOutlineConfiguration extends XMLQuickOutlineConfiguration { + /** Constructs a new {@link AndroidQuickOutlineConfiguration} */ public AndroidQuickOutlineConfiguration() { } @Override public ILabelProvider getLabelProvider() { - return new JFaceNodeLabelProvider() { - @Override - public Image getImage(Object element) { - if (element instanceof Element) { - Element e = (Element) element; - String tagName = e.getTagName(); - IconFactory factory = IconFactory.getInstance(); - Image img = factory.getIcon(tagName); - if (img != null) { - return img; - } - } - return super.getImage(element); - } - - @Override - public String getText(Object element) { - String text = super.getText(element); - if (element instanceof Element) { - Element e = (Element) element; - String id = e.getAttributeNS(ANDROID_URI, ATTR_ID); - if (id == null || id.length() == 0) { - id = e.getAttributeNS(ANDROID_URI, ATTR_NAME); - } - if (id == null || id.length() == 0) { - id = e.getAttribute(ATTR_NAME); - } - if (id != null && id.length() > 0) { - return text + ": " + id; //$NON-NLS-1$ - } - } - return text; - } - }; + return new OutlineLabelProvider(); } - } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java index 7c57549..58e156e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java @@ -76,6 +76,7 @@ import org.eclipse.ui.ide.IGotoMarker; import org.eclipse.ui.internal.browser.WorkbenchBrowserSupport; import org.eclipse.ui.part.MultiPageEditorPart; import org.eclipse.ui.part.WorkbenchPart; +import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IModelManager; import org.eclipse.wst.sse.core.internal.provisional.IModelStateListener; @@ -273,6 +274,10 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh }; } + if (result == null && adapter == IContentOutlinePage.class) { + return getStructuredTextEditor().getAdapter(adapter); + } + return result; } @@ -416,7 +421,6 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh } } - /** * Notifies this multi-page editor that the page with the given id has been * activated. This method is called when the user selects a different tab. @@ -445,6 +449,15 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh } /** + * Returns true if the active page is the editor page + * + * @return true if the active page is the editor page + */ + public boolean isEditorPageActive() { + return getActivePage() == mTextPageIndex; + } + + /** * Notifies this listener that some resource changes * are happening, or have already happened. * diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java index ca13d38..22aa687 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java @@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.internal.editors; import com.android.annotations.NonNull; +import com.android.annotations.Nullable; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.sdklib.SdkConstants; @@ -181,10 +182,13 @@ public class IconFactory { * in the editor's "icons" directory. If it doesn't exist, the * fallback will be used instead. * @param fallback the fallback icon name to use if the primary icon does - * not exist. - * @return the icon, which should not be disposed by the caller + * not exist, or null if the method should return null if the + * image does not exist + * @return the icon, which should not be disposed by the caller, or null + * if the image does not exist *and* */ - public Image getIcon(String osName, String fallback) { + @Nullable + public Image getIcon(@NonNull String osName, @Nullable String fallback) { String key = osName; Image icon = mIconMap.get(key); if (icon == null && !mIconMap.containsKey(key)) { @@ -200,21 +204,26 @@ public class IconFactory { } /** - * Returns an icon of the given name, or if that image does not exist and icon - * of the given fallback name. + * Returns an icon of the given name, or if that image does not exist and + * icon of the given fallback name. * * @param key the icon name - * @param fallbackKey the fallback image to use if the primary key does not exist - * @return the image descriptor + * @param fallbackKey the fallback image to use if the primary key does not + * exist + * @return the image descriptor, or null if the image does not exist and the + * fallbackKey is null */ - @NonNull - public ImageDescriptor getImageDescriptor(@NonNull String key, @NonNull String fallbackKey) { + @Nullable + public ImageDescriptor getImageDescriptor(@NonNull String key, @Nullable String fallbackKey) { ImageDescriptor id = mImageDescMap.get(key); if (id == null && !mImageDescMap.containsKey(key)) { id = AbstractUIPlugin.imageDescriptorFromPlugin( AdtPlugin.PLUGIN_ID, String.format("/icons/%1$s.png", key)); //$NON-NLS-1$ if (id == null) { + if (fallbackKey == null) { + return null; + } id = getImageDescriptor(fallbackKey); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/OutlineLabelProvider.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/OutlineLabelProvider.java new file mode 100644 index 0000000..ea3f066 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/OutlineLabelProvider.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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 com.android.ide.eclipse.adt.internal.editors; + +import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI; +import static com.android.ide.common.layout.LayoutConstants.ATTR_ID; +import static com.android.ide.common.layout.LayoutConstants.ATTR_NAME; +import static com.android.ide.common.layout.LayoutConstants.ATTR_SRC; +import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT; +import static com.android.ide.common.layout.LayoutConstants.DRAWABLE_PREFIX; +import static com.android.ide.common.layout.LayoutConstants.LAYOUT_PREFIX; + +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors; + +import org.eclipse.swt.graphics.Image; +import org.eclipse.wst.xml.ui.internal.contentoutline.JFaceNodeLabelProvider; +import org.w3c.dom.Element; + +/** Label provider for the XML outlines and quick outlines: Use our own icons, + * when available, and and include the most important attribute (id, name, or text) */ +@SuppressWarnings("restriction") // XML UI API +class OutlineLabelProvider extends JFaceNodeLabelProvider { + @Override + public Image getImage(Object element) { + if (element instanceof Element) { + Element e = (Element) element; + String tagName = e.getTagName(); + IconFactory factory = IconFactory.getInstance(); + Image img = factory.getIcon(tagName, null); + if (img != null) { + return img; + } + } + return super.getImage(element); + } + + @Override + public String getText(Object element) { + String text = super.getText(element); + if (element instanceof Element) { + Element e = (Element) element; + String id = e.getAttributeNS(ANDROID_URI, ATTR_ID); + if (id == null || id.length() == 0) { + id = e.getAttributeNS(ANDROID_URI, ATTR_NAME); + if (id == null || id.length() == 0) { + id = e.getAttribute(ATTR_NAME); + if (id == null || id.length() == 0) { + id = e.getAttributeNS(ANDROID_URI, ATTR_TEXT); + if (id != null && id.length() > 15) { + id = id.substring(0, 12) + "..."; + } + if (id == null || id.length() == 0) { + id = e.getAttributeNS(ANDROID_URI, ATTR_SRC); + if (id != null && id.length() > 0) { + if (id.startsWith(DRAWABLE_PREFIX)) { + id = id.substring(DRAWABLE_PREFIX.length()); + } + } else { + id = e.getAttribute(LayoutDescriptors.ATTR_LAYOUT); + if (id != null && id.length() > 0) { + if (id.startsWith(LAYOUT_PREFIX)) { + id = id.substring(LAYOUT_PREFIX.length()); + } + } + } + } + } + } + } + + if (id != null && id.length() > 0) { + return text + ": " + id; //$NON-NLS-1$ + } + } + return text; + } +}
\ No newline at end of file diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java new file mode 100644 index 0000000..4ccab2d --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/XmlEditorMultiOutline.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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 com.android.ide.eclipse.adt.internal.editors; + +import com.android.ide.eclipse.adt.AdtPlugin; + +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.part.IPageBookViewPage; +import org.eclipse.ui.part.Page; +import org.eclipse.ui.part.PageBook; +import org.eclipse.ui.views.contentoutline.IContentOutlinePage; + +import java.util.ArrayList; +import java.util.List; + +/** + * Outline used for XML editors that have multiple pages with separate outlines: + * switches between them + * <p> + * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=1917 + * <p> + * Modeled after .org.eclipse.pde.internal.ui.editor.PDEMultiPageContentOutline + */ +public class XmlEditorMultiOutline extends Page implements IContentOutlinePage, + ISelectionChangedListener { + private boolean mDisposed; + private PageBook mPageBook; + private IContentOutlinePage mCurrentPage; + private IActionBars mActionBars; + private IContentOutlinePage mEmptyPage; + private List<ISelectionChangedListener> mListeners; + private ISelection mSelection; + + public XmlEditorMultiOutline() { + } + + @Override + public Control getControl() { + return mPageBook; + } + + @Override + public void createControl(Composite parent) { + mPageBook = new PageBook(parent, SWT.NONE); + } + + @Override + public void dispose() { + mDisposed = true; + mListeners = null; + if (mPageBook != null && !mPageBook.isDisposed()) { + mPageBook.dispose(); + mPageBook = null; + } + if (mEmptyPage != null) { + mEmptyPage.dispose(); + mEmptyPage = null; + } + } + + public boolean isDisposed() { + return mDisposed; + } + + @Override + public void makeContributions(IMenuManager menuManager, IToolBarManager toolBarManager, + IStatusLineManager statusLineManager) { + } + + @Override + public void setActionBars(IActionBars actionBars) { + mActionBars = actionBars; + if (mCurrentPage != null) { + setPageActive(mCurrentPage); + } + } + + @Override + public void setFocus() { + if (mCurrentPage != null) { + mCurrentPage.setFocus(); + } + } + + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + if (mListeners == null) { + mListeners = new ArrayList<ISelectionChangedListener>(); + } + mListeners.add(listener); + } + + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + mListeners.remove(listener); + } + + @Override + public ISelection getSelection() { + return mSelection; + } + + @Override + public void selectionChanged(SelectionChangedEvent event) { + setSelection(event.getSelection()); + } + + public void setPageActive(IContentOutlinePage page) { + if (page == null) { + if (mEmptyPage == null) { + mEmptyPage = new EmptyPage(); + } + page = mEmptyPage; + } + if (mCurrentPage != null) { + mCurrentPage.removeSelectionChangedListener(this); + } + page.addSelectionChangedListener(this); + mCurrentPage = page; + // Still initializing? + if (mPageBook == null) { + return; + } + Control control = page.getControl(); + if (control == null || control.isDisposed()) { + if (page instanceof IPageBookViewPage) { + try { + ((IPageBookViewPage) page).init(getSite()); + } catch (PartInitException e) { + AdtPlugin.log(e, null); + } + } + page.createControl(mPageBook); + page.setActionBars(mActionBars); + control = page.getControl(); + } + mPageBook.showPage(control); + } + + @Override + public void setSelection(ISelection selection) { + mSelection = selection; + if (mListeners != null) { + SelectionChangedEvent e = new SelectionChangedEvent(this, selection); + for (int i = 0; i < mListeners.size(); i++) { + mListeners.get(i).selectionChanged(e); + } + } + } + + private static class EmptyPage implements IContentOutlinePage { + private Composite mControl; + + private EmptyPage() { + } + + @Override + public void createControl(Composite parent) { + mControl = new Composite(parent, SWT.NULL); + } + + @Override + public void dispose() { + } + + @Override + public Control getControl() { + return mControl; + } + + @Override + public void setActionBars(IActionBars actionBars) { + } + + @Override + public void setFocus() { + } + + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + } + + @Override + public ISelection getSelection() { + return StructuredSelection.EMPTY; + } + + @Override + public void removeSelectionChangedListener(ISelectionChangedListener listener) { + } + + @Override + public void setSelection(ISelection selection) { + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlDelegate.java index a664800..08bdcf9 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlDelegate.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlDelegate.java @@ -29,8 +29,10 @@ import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IURIEditorInput; +import org.eclipse.ui.forms.editor.IFormPage; import org.eclipse.ui.part.EditorActionBarContributor; import org.eclipse.ui.part.FileEditorInput; +import org.eclipse.ui.part.MultiPageEditorPart; import org.w3c.dom.Document; /** @@ -201,4 +203,18 @@ public abstract class CommonXmlDelegate { public boolean delegateSupportsFormatOnGuiEdit() { return false; } + + /** + * Called after the editor's active page has been set. + * + * @param superReturned the return value from + * {@link MultiPageEditorPart#setActivePage(int)} + * @param pageIndex the index of the page to be activated; the index must be + * valid + * @return the page, or null + * @see MultiPageEditorPart#setActivePage(int) + */ + public IFormPage delegatePostSetActivePage(IFormPage superReturned, String pageIndex) { + return superReturned; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlEditor.java index 96ce82b..68c7807 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/common/CommonXmlEditor.java @@ -47,6 +47,7 @@ import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IShowEditorInput; import org.eclipse.ui.IURIEditorInput; import org.eclipse.ui.PartInitException; +import org.eclipse.ui.forms.editor.IFormPage; import org.eclipse.ui.ide.IDE; import org.w3c.dom.Document; @@ -372,6 +373,17 @@ public class CommonXmlEditor extends AndroidXmlEditor implements IShowEditorInpu } } + @Override + public IFormPage setActivePage(String pageId) { + IFormPage page = super.setActivePage(pageId); + + if (mDelegate != null) { + return mDelegate.delegatePostSetActivePage(page, pageId); + } + + return page; + } + /* Implements showEditorInput(...) in IShowEditorInput */ @Override public void showEditorInput(IEditorInput editorInput) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java index 9c7189b..df39a3d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditorDelegate.java @@ -16,11 +16,13 @@ package com.android.ide.eclipse.adt.internal.editors.layout; +import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.annotations.VisibleForTesting; import com.android.annotations.VisibleForTesting.Visibility; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.editors.XmlEditorMultiOutline; import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlDelegate; import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor; @@ -51,19 +53,25 @@ import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IPartListener; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.ISelectionService; import org.eclipse.ui.IShowEditorInput; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; +import org.eclipse.ui.forms.editor.IFormPage; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.eclipse.ui.views.properties.IPropertySheetPage; +import org.eclipse.wst.sse.ui.StructuredTextEditor; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -103,8 +111,24 @@ public class LayoutEditorDelegate extends CommonXmlDelegate private GraphicalEditorPart mGraphicalEditor; private int mGraphicalEditorIndex; + /** Implementation of the {@link IContentOutlinePage} for this editor */ - private IContentOutlinePage mOutline; + private OutlinePage mLayoutOutline; + + /** The XML editor outline */ + private IContentOutlinePage mEditorOutline; + + /** Multiplexing outline, used for multi-page editors that have their own outline */ + private XmlEditorMultiOutline mMultiOutline; + + /** + * Temporary flag set by the editor caret listener which is used to cause + * the next getAdapter(IContentOutlinePage.class) call to return the editor + * outline rather than the multi-outline. See the {@link #delegateGetAdapter} + * method for details. + */ + private boolean mCheckOutlineAdapter; + /** Custom implementation of {@link IPropertySheetPage} for this editor */ private IPropertySheetPage mPropertyPage; @@ -402,16 +426,60 @@ public class LayoutEditorDelegate extends CommonXmlDelegate */ @Override public Object delegateGetAdapter(Class<?> adapter) { - // For the outline, force it to come from the Graphical Editor. - // This fixes the case where a layout file is opened in XML view first and the outline - // gets stuck in the XML outline. - if (IContentOutlinePage.class == adapter && mGraphicalEditor != null) { + if (adapter == IContentOutlinePage.class) { + // Somebody has requested the outline. Eclipse can only have a single outline page, + // even for a multi-part editor: + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=1917 + // To work around this we use PDE's workaround of having a single multiplexing + // outline which switches its contents between the outline pages we register + // for it, and then on page switch we notify it to update itself. + + // There is one complication: The XML editor outline listens for the editor + // selection and uses this to automatically expand its tree children and show + // the current node containing the caret as selected. Unfortunately, this + // listener code contains this: + // + // /* Bug 136310, unless this page is that part's + // * IContentOutlinePage, ignore the selection change */ + // if (part.getAdapter(IContentOutlinePage.class) == this) { + // + // This means that when we return the multiplexing outline from this getAdapter + // method, the outline no longer updates to track the selection. + // To work around this, we use the following hack^H^H^H^H technique: + // - Add a selection listener *before* requesting the editor outline, such + // that the selection listener is told about the impending selection event + // right before the editor outline hears about it. Set the flag + // mCheckOutlineAdapter to true. (We also only set it if the editor view + // itself is active.) + // - In this getAdapter method, when somebody requests the IContentOutline.class, + // see if mCheckOutlineAdapter to see if this request is *likely* coming + // from the XML editor outline. If so, make sure it is by actually looking + // at the signature of the caller. If it's the editor outline, then return + // the editor outline instance itself rather than the multiplexing outline. + if (mCheckOutlineAdapter && mEditorOutline != null) { + mCheckOutlineAdapter = false; + // Make *sure* this is really the editor outline calling in case + // future versions of Eclipse changes the sequencing or dispatch of selection + // events: + StackTraceElement[] frames = new Throwable().fillInStackTrace().getStackTrace(); + if (frames.length > 2) { + StackTraceElement frame = frames[2]; + if (frame.getClassName().equals( + "org.eclipse.wst.sse.ui.internal.contentoutline." + //$NON-NLS-1$ + "ConfigurableContentOutlinePage$PostSelectionServiceListener")) { //$NON-NLS-1$ + return mEditorOutline; + } + } + } - if (mOutline == null && mGraphicalEditor != null) { - mOutline = new OutlinePage(mGraphicalEditor); + // Use a multiplexing outline: workaround for + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=1917 + if (mMultiOutline == null || mMultiOutline.isDisposed()) { + mMultiOutline = new XmlEditorMultiOutline(); + updateOutline(getEditor().getActivePageInstance()); } - return mOutline; + return mMultiOutline; } if (IPropertySheetPage.class == adapter && mGraphicalEditor != null) { @@ -426,6 +494,60 @@ public class LayoutEditorDelegate extends CommonXmlDelegate return super.delegateGetAdapter(adapter); } + /** + * Update the contents of the outline to show either the XML editor outline + * or the layout editor graphical outline depending on which tab is visible + */ + private void updateOutline(IFormPage page) { + if (mMultiOutline == null) { + return; + } + + IContentOutlinePage outline; + CommonXmlEditor editor = getEditor(); + if (!editor.isEditorPageActive()) { + outline = getGraphicalOutline(); + } else { + // Use plain XML editor outline instead + if (mEditorOutline == null) { + StructuredTextEditor structuredTextEditor = editor.getStructuredTextEditor(); + if (structuredTextEditor != null) { + IWorkbenchWindow window = editor.getSite().getWorkbenchWindow(); + ISelectionService service = window.getSelectionService(); + service.addPostSelectionListener(new ISelectionListener() { + @Override + public void selectionChanged(IWorkbenchPart part, ISelection selection) { + if (getEditor().isEditorPageActive()) { + mCheckOutlineAdapter = true; + } + } + }); + + mEditorOutline = (IContentOutlinePage) structuredTextEditor.getAdapter( + IContentOutlinePage.class); + } + } + + outline = mEditorOutline; + } + + mMultiOutline.setPageActive(outline); + } + + /** + * Returns the graphical outline associated with the layout editor + * + * @return the outline page, never null + */ + @NonNull + public OutlinePage getGraphicalOutline() { + if (mLayoutOutline == null) { + mLayoutOutline = new OutlinePage(mGraphicalEditor); + } + + return mLayoutOutline; + } + @Override public void delegatePageChange(int newPageIndex) { if (getEditor().getCurrentPage() == getEditor().getTextPageIndex() && @@ -454,6 +576,24 @@ public class LayoutEditorDelegate extends CommonXmlDelegate } } + @Override + public void delegatePostPageChange(int newPageIndex) { + super.delegatePostPageChange(newPageIndex); + + IFormPage page = getEditor().getActivePageInstance(); + updateOutline(page); + } + + @Override + public IFormPage delegatePostSetActivePage(IFormPage superReturned, String pageIndex) { + IFormPage page = superReturned; + if (page != null) { + updateOutline(page); + } + + return page; + } + // ----- IActionContributorDelegate methods ---- @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java index e45803a..8913850 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java @@ -91,7 +91,6 @@ import org.eclipse.ui.actions.ContributionItemFactory; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; import org.eclipse.ui.texteditor.ITextEditor; -import org.eclipse.ui.views.contentoutline.IContentOutlinePage; import org.w3c.dom.Node; import java.util.HashSet; @@ -305,10 +304,8 @@ public class LayoutCanvas extends Canvas { // --- setup outline --- // Get the outline associated with this editor, if any and of the right type. - Object outline = editorDelegate == null ? null : - editorDelegate.delegateGetAdapter(IContentOutlinePage.class); - if (outline instanceof OutlinePage) { - mOutlinePage = (OutlinePage) outline; + if (editorDelegate != null) { + mOutlinePage = editorDelegate.getGraphicalOutline(); } } |