| /* |
| * 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.common; |
| |
| import com.android.ide.common.resources.ResourceFolder; |
| import com.android.ide.eclipse.adt.AdtConstants; |
| import com.android.ide.eclipse.adt.AdtPlugin; |
| import com.android.ide.eclipse.adt.AdtUtils; |
| import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; |
| import com.android.ide.eclipse.adt.internal.editors.animator.AnimationEditorDelegate; |
| import com.android.ide.eclipse.adt.internal.editors.color.ColorEditorDelegate; |
| import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlDelegate.IDelegateCreator; |
| import com.android.ide.eclipse.adt.internal.editors.drawable.DrawableEditorDelegate; |
| import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; |
| import com.android.ide.eclipse.adt.internal.editors.menu.MenuEditorDelegate; |
| import com.android.ide.eclipse.adt.internal.editors.otherxml.OtherXmlEditorDelegate; |
| import com.android.ide.eclipse.adt.internal.editors.otherxml.PlainXmlEditorDelegate; |
| import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; |
| import com.android.ide.eclipse.adt.internal.editors.values.ValuesEditorDelegate; |
| import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; |
| import com.android.resources.ResourceFolderType; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jface.text.source.ISourceViewer; |
| import org.eclipse.jface.text.source.ISourceViewerExtension2; |
| import org.eclipse.ui.IEditorDescriptor; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorSite; |
| 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; |
| |
| /** |
| * Multi-page form editor for ALL /res XML files. |
| * <p/> |
| * This editor doesn't actually do anything. Instead, it defers actual implementation |
| * to {@link CommonXmlDelegate} instances. |
| */ |
| public class CommonXmlEditor extends AndroidXmlEditor implements IShowEditorInput { |
| |
| public static final String ID = AdtConstants.EDITORS_NAMESPACE + ".CommonXmlEditor"; //$NON-NLS-1$ |
| |
| /** |
| * Registered {@link CommonXmlDelegate}s. |
| * All delegates must have a {@code Creator} class which is instantiated |
| * once here statically. All the creators are invoked in the order they |
| * are defined and the first one to return a non-null delegate is used. |
| */ |
| private static final IDelegateCreator[] DELEGATES = { |
| new LayoutEditorDelegate.Creator(), |
| new ValuesEditorDelegate.Creator(), |
| new AnimationEditorDelegate.Creator(), |
| new ColorEditorDelegate.Creator(), |
| new DrawableEditorDelegate.Creator(), |
| new MenuEditorDelegate.Creator(), |
| new OtherXmlEditorDelegate.Creator(), |
| }; |
| |
| /** |
| * IDs of legacy editors replaced by the {@link CommonXmlEditor}. |
| */ |
| public static final String[] LEGACY_EDITOR_IDS = { |
| LayoutEditorDelegate.LEGACY_EDITOR_ID, |
| ValuesEditorDelegate.LEGACY_EDITOR_ID, |
| AnimationEditorDelegate.LEGACY_EDITOR_ID, |
| ColorEditorDelegate.LEGACY_EDITOR_ID, |
| DrawableEditorDelegate.LEGACY_EDITOR_ID, |
| MenuEditorDelegate.LEGACY_EDITOR_ID, |
| OtherXmlEditorDelegate.LEGACY_EDITOR_ID, |
| }; |
| |
| private CommonXmlDelegate mDelegate = null; |
| |
| /** |
| * Creates the form editor for resources XML files. |
| */ |
| public CommonXmlEditor() { |
| super(); |
| } |
| |
| @Override |
| public void init(IEditorSite site, final IEditorInput editorInput) |
| throws PartInitException { |
| if (editorInput instanceof IFileEditorInput) { |
| |
| IFileEditorInput fileInput = (IFileEditorInput) editorInput; |
| IFile file = fileInput.getFile(); |
| |
| // Adjust the default file editor ID |
| |
| IEditorDescriptor file_desc = IDE.getDefaultEditor(file); |
| String id = file_desc == null ? null : file_desc.getId(); |
| boolean mustChange = id != null && |
| !id.equals(ID) && |
| id.startsWith(AdtConstants.EDITORS_NAMESPACE); |
| if (!mustChange) { |
| // Maybe this was opened by a manual Open With with a legacy ID? |
| id = site.getId(); |
| mustChange = id != null && |
| !id.equals(ID) && |
| id.startsWith(AdtConstants.EDITORS_NAMESPACE); |
| } |
| |
| if (mustChange) { |
| // It starts by our editor namespace but it's not the right ID. |
| // This is an old Android XML ID. Change it to our new ID. |
| IDE.setDefaultEditor(file, ID); |
| AdtPlugin.log(IStatus.INFO, |
| "Changed legacy editor ID %s for %s", //$NON-NLS-1$ |
| id, |
| file.getFullPath()); |
| } |
| |
| // Now find the delegate for the file. |
| |
| ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file); |
| ResourceFolderType type = resFolder == null ? null : resFolder.getType(); |
| |
| if (type == null) { |
| // We lack any real resource information about that file. |
| // Let's take a guess using the actual path. |
| String folderName = AdtUtils.getParentFolderName(editorInput); |
| type = ResourceFolderType.getFolderType(folderName); |
| } |
| |
| if (type != null) { |
| for (IDelegateCreator creator : DELEGATES) { |
| mDelegate = creator.createForFile(this, type); |
| if (mDelegate != null) { |
| break; |
| } |
| } |
| } |
| |
| if (mDelegate == null) { |
| // We didn't find any editor. |
| // We'll use the PlainXmlEditorDelegate as a catch-all editor. |
| AdtPlugin.log(IStatus.INFO, |
| "No valid Android XML Editor Delegate found for file %1$s [Res %2$s, type %3$s]", |
| file.getFullPath(), |
| resFolder, |
| type); |
| mDelegate = new PlainXmlEditorDelegate(this); |
| } |
| } else if (editorInput instanceof IURIEditorInput) { |
| String folderName = AdtUtils.getParentFolderName(editorInput); |
| ResourceFolderType type = ResourceFolderType.getFolderType(folderName); |
| if (type == ResourceFolderType.LAYOUT) { |
| // The layout editor has a lot of hardcoded requirements for real IFiles |
| // and IProjects so for now just use a plain XML editor for project-less layout |
| // files |
| mDelegate = new OtherXmlEditorDelegate(this); |
| } else if (type != null) { |
| for (IDelegateCreator creator : DELEGATES) { |
| mDelegate = creator.createForFile(this, type); |
| if (mDelegate != null) { |
| break; |
| } |
| } |
| } |
| |
| if (mDelegate == null) { |
| // We didn't find any editor. |
| // We'll use the PlainXmlEditorDelegate as a catch-all editor. |
| AdtPlugin.log(IStatus.INFO, |
| "No valid Android XML Editor Delegate found for file %1$s [Res %2$s, type %3$s]", |
| ((IURIEditorInput) editorInput).getURI().toString(), |
| folderName, |
| type); |
| mDelegate = new PlainXmlEditorDelegate(this); |
| } |
| } |
| |
| if (mDelegate == null) { |
| // We can't do anything if we don't have a valid file. |
| AdtPlugin.log(IStatus.INFO, |
| "Android XML Editor cannot process non-file input %1$s", //$NON-NLS-1$ |
| (editorInput == null ? "null" : editorInput.toString())); //$NON-NLS-1$ |
| throw new PartInitException("Android XML Editor cannot process this input."); |
| } else { |
| // Invoke the editor's init after setting up the delegate. This will call setInput(). |
| super.init(site, editorInput); |
| } |
| } |
| |
| /** |
| * @return The root node of the UI element hierarchy |
| */ |
| @Override |
| public UiElementNode getUiRootNode() { |
| return mDelegate == null ? null : mDelegate.getUiRootNode(); |
| } |
| |
| public CommonXmlDelegate getDelegate() { |
| return mDelegate; |
| } |
| |
| // ---- Base Class Overrides ---- |
| |
| @Override |
| public void dispose() { |
| if (mDelegate != null) { |
| mDelegate.dispose(); |
| } |
| |
| super.dispose(); |
| } |
| |
| /** |
| * Save the XML. |
| * <p/> |
| * The actual save operation is done in the super class by committing |
| * all data to the XML model and then having the Structured XML Editor |
| * save the XML. |
| * <p/> |
| * Here we just need to tell the delegate that the model has |
| * been saved. |
| */ |
| @Override |
| public void doSave(IProgressMonitor monitor) { |
| super.doSave(monitor); |
| if (mDelegate != null) { |
| mDelegate.delegateDoSave(monitor); |
| } |
| } |
| |
| /** |
| * Returns whether the "save as" operation is supported by this editor. |
| * <p/> |
| * Save-As is a valid operation for the ManifestEditor since it acts on a |
| * single source file. |
| * |
| * @see IEditorPart |
| */ |
| @Override |
| public boolean isSaveAsAllowed() { |
| return mDelegate == null ? false : mDelegate.isSaveAsAllowed(); |
| } |
| |
| /** |
| * Create the various form pages. |
| */ |
| @Override |
| protected void createFormPages() { |
| if (mDelegate != null) { |
| mDelegate.delegateCreateFormPages(); |
| } |
| } |
| |
| @Override |
| protected void postCreatePages() { |
| super.postCreatePages(); |
| |
| if (mDelegate != null) { |
| mDelegate.delegatePostCreatePages(); |
| } |
| } |
| |
| @Override |
| protected void addPages() { |
| // Create the editor pages. |
| // This will also create the EditorPart. |
| super.addPages(); |
| |
| // When the EditorPart is being created, it configures the SourceViewer |
| // and will try to use our CommonSourceViewerConfig. Our config needs to |
| // know which ContentAssist processor to use (since we have one per resource |
| // folder type) but it doesn't have the necessary info to do so. |
| // Consequently, once the part is created, we can now unconfigure the source |
| // viewer and reconfigure it with the right settings. |
| ISourceViewer ssv = getStructuredSourceViewer(); |
| if (mDelegate != null && ssv instanceof ISourceViewerExtension2) { |
| ((ISourceViewerExtension2) ssv).unconfigure(); |
| ssv.configure(new CommonSourceViewerConfig( |
| mDelegate.getAndroidContentAssistProcessor())); |
| } |
| } |
| |
| /* (non-java doc) |
| * Change the tab/title name to include the name of the layout. |
| */ |
| @Override |
| protected void setInput(IEditorInput input) { |
| super.setInput(input); |
| assert mDelegate != null; |
| if (mDelegate != null) { |
| mDelegate.delegateSetInput(input); |
| } |
| } |
| |
| @Override |
| public void setInputWithNotify(IEditorInput input) { |
| super.setInputWithNotify(input); |
| if (mDelegate instanceof LayoutEditorDelegate) { |
| ((LayoutEditorDelegate) mDelegate).delegateSetInputWithNotify(input); |
| } |
| } |
| |
| /** |
| * Processes the new XML Model, which XML root node is given. |
| * |
| * @param xml_doc The XML document, if available, or null if none exists. |
| */ |
| @Override |
| protected void xmlModelChanged(Document xml_doc) { |
| if (mDelegate != null) { |
| mDelegate.delegateXmlModelChanged(xml_doc); |
| } |
| } |
| |
| @Override |
| protected Job runLint() { |
| if (mDelegate != null && getEditorInput() instanceof IFileEditorInput) { |
| return mDelegate.delegateRunLint(); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the custom IContentOutlinePage or IPropertySheetPage when asked for it. |
| */ |
| @Override |
| public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { |
| if (mDelegate != null) { |
| Object value = mDelegate.delegateGetAdapter(adapter); |
| if (value != null) { |
| return value; |
| } |
| } |
| |
| // return default |
| return super.getAdapter(adapter); |
| } |
| |
| @Override |
| protected void pageChange(int newPageIndex) { |
| if (mDelegate != null) { |
| mDelegate.delegatePageChange(newPageIndex); |
| } |
| |
| super.pageChange(newPageIndex); |
| |
| if (mDelegate != null) { |
| mDelegate.delegatePostPageChange(newPageIndex); |
| } |
| } |
| |
| @Override |
| protected int getPersistenceCategory() { |
| if (mDelegate != null) { |
| return mDelegate.delegateGetPersistenceCategory(); |
| } |
| return CATEGORY_OTHER; |
| } |
| |
| @Override |
| public void initUiRootNode(boolean force) { |
| if (mDelegate != null) { |
| mDelegate.delegateInitUiRootNode(force); |
| } |
| } |
| |
| @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) { |
| if (mDelegate instanceof LayoutEditorDelegate) { |
| ((LayoutEditorDelegate) mDelegate).showEditorInput(editorInput); |
| } |
| } |
| |
| @Override |
| public boolean supportsFormatOnGuiEdit() { |
| if (mDelegate != null) { |
| return mDelegate.delegateSupportsFormatOnGuiEdit(); |
| } |
| return super.supportsFormatOnGuiEdit(); |
| } |
| |
| @Override |
| public void activated() { |
| super.activated(); |
| if (mDelegate != null) { |
| mDelegate.delegateActivated(); |
| } |
| } |
| |
| @Override |
| public void deactivated() { |
| super.deactivated(); |
| if (mDelegate != null) { |
| mDelegate.delegateDeactivated(); |
| } |
| } |
| |
| @Override |
| public String getPartName() { |
| if (mDelegate != null) { |
| String name = mDelegate.delegateGetPartName(); |
| if (name != null) { |
| return name; |
| } |
| } |
| |
| return super.getPartName(); |
| } |
| |
| // -------------------- |
| // Base methods exposed so that XmlEditorDelegate can access them |
| |
| @Override |
| public void setPartName(String partName) { |
| super.setPartName(partName); |
| } |
| |
| @Override |
| public void setPageText(int pageIndex, String text) { |
| super.setPageText(pageIndex, text); |
| } |
| |
| @Override |
| public void firePropertyChange(int propertyId) { |
| super.firePropertyChange(propertyId); |
| } |
| |
| @Override |
| public int getPageCount() { |
| return super.getPageCount(); |
| } |
| |
| @Override |
| public int getCurrentPage() { |
| return super.getCurrentPage(); |
| } |
| } |