aboutsummaryrefslogtreecommitdiffstats
path: root/eclipse/plugins/com.android.ide.eclipse.adt
diff options
context:
space:
mode:
authorRaphael Moll <ralf@android.com>2010-06-14 15:31:21 -0700
committerRaphael Moll <ralf@android.com>2010-06-14 16:44:23 -0700
commitb9dd664f2996e4090603e3fc224b98453a49f759 (patch)
treefc689b801384b7950aab539636facafaf0eb6e5a /eclipse/plugins/com.android.ide.eclipse.adt
parent50ec6d9b0ff6dcbb8aec2b081d927a89527696fe (diff)
downloadsdk-b9dd664f2996e4090603e3fc224b98453a49f759.zip
sdk-b9dd664f2996e4090603e3fc224b98453a49f759.tar.gz
sdk-b9dd664f2996e4090603e3fc224b98453a49f759.tar.bz2
ADT: Editor for export.properties in export-projects.
This is an initial framework that needs to be refined. Change-Id: I2cd8a7708c30826075de076d2b5826ed8af77eb9
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml8
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidTextEditor.java572
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/AbstractPropertiesFieldsPart.java351
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportEditor.java107
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportFieldsPart.java100
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportLinksPart.java121
-rwxr-xr-xeclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportPropertiesPage.java113
7 files changed, 1372 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index 130abe8..78ebac3 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -440,6 +440,14 @@
name="Android Manifest Editor">
</editor>
<editor
+ class="com.android.ide.eclipse.adt.internal.editors.export.ExportEditor"
+ default="true"
+ filenames="export.properties"
+ icon="icons/android_file.png"
+ id="com.android.ide.eclipse.editors.export.ExportEditor"
+ name="Android Export Editor">
+ </editor>
+ <editor
class="com.android.ide.eclipse.adt.internal.editors.resources.ResourcesEditor"
default="false"
extensions="xml"
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidTextEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidTextEditor.java
new file mode 100755
index 0000000..d180b5e
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidTextEditor.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2010 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.core.internal.filebuffers.SynchronizableDocument;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.DocumentRewriteSession;
+import org.eclipse.jface.text.DocumentRewriteSessionType;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension4;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.actions.ActionFactory;
+import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
+import org.eclipse.ui.editors.text.TextEditor;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.IFormPage;
+import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.events.IHyperlinkListener;
+import org.eclipse.ui.forms.widgets.FormText;
+import org.eclipse.ui.internal.browser.WorkbenchBrowserSupport;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.ui.part.MultiPageEditorPart;
+import org.eclipse.ui.part.WorkbenchPart;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.wst.sse.ui.StructuredTextEditor;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Multi-page form editor for Android text files.
+ * <p/>
+ * It is designed to work with a {@link TextEditor} that will display a text file.
+ * <br/>
+ * Derived classes must implement createFormPages to create the forms before the
+ * source editor. This can be a no-op if desired.
+ */
+public abstract class AndroidTextEditor extends FormEditor implements IResourceChangeListener {
+
+ /** Preference name for the current page of this file */
+ private static final String PREF_CURRENT_PAGE = "_current_page";
+
+ /** Id string used to create the Android SDK browser */
+ private static String BROWSER_ID = "android"; // $NON-NLS-1$
+
+ /** Page id of the XML source editor, used for switching tabs programmatically */
+ public final static String TEXT_EDITOR_ID = "editor_part"; //$NON-NLS-1$
+
+ /** Width hint for text fields. Helps the grid layout resize properly on smaller screens */
+ public static final int TEXT_WIDTH_HINT = 50;
+
+ /** Page index of the text editor (always the last page) */
+ private int mTextPageIndex;
+
+ /** The text editor */
+ private TextEditor mTextEditor;
+
+ /** flag set during page creation */
+ private boolean mIsCreatingPage = false;
+
+ private IDocument mDocument;
+
+ /**
+ * Creates a form editor.
+ */
+ public AndroidTextEditor() {
+ super();
+ }
+
+ // ---- Abstract Methods ----
+
+ /**
+ * Creates the various form pages.
+ * <p/>
+ * Derived classes must implement this to add their own specific tabs.
+ */
+ abstract protected void createFormPages();
+
+ /**
+ * Called by the base class {@link AndroidTextEditor} once all pages (custom form pages
+ * as well as text editor page) have been created. This give a chance to deriving
+ * classes to adjust behavior once the text page has been created.
+ */
+ protected void postCreatePages() {
+ // Nothing in the base class.
+ }
+
+ /**
+ * Subclasses should override this method to process the new text model.
+ * This is called after the document has been edited.
+ *
+ * The base implementation is empty.
+ *
+ * @param event Specification of changes applied to document.
+ */
+ protected void onDocumentChanged(DocumentEvent event) {
+ // pass
+ }
+
+ // ---- Base Class Overrides, Interfaces Implemented ----
+
+ /**
+ * Creates the pages of the multi-page editor.
+ */
+ @Override
+ protected void addPages() {
+ createAndroidPages();
+ selectDefaultPage(null /* defaultPageId */);
+ }
+
+ /**
+ * Creates the page for the Android Editors
+ */
+ protected void createAndroidPages() {
+ mIsCreatingPage = true;
+ createFormPages();
+ createTextEditor();
+ createUndoRedoActions();
+ postCreatePages();
+ mIsCreatingPage = false;
+ }
+
+ /**
+ * Returns whether the editor is currently creating its pages.
+ */
+ public boolean isCreatingPages() {
+ return mIsCreatingPage;
+ }
+
+ /**
+ * Creates undo redo actions for the editor site (so that it works for any page of this
+ * multi-page editor) by re-using the actions defined by the {@link TextEditor}
+ * (aka the XML text editor.)
+ */
+ private void createUndoRedoActions() {
+ IActionBars bars = getEditorSite().getActionBars();
+ if (bars != null) {
+ IAction action = mTextEditor.getAction(ActionFactory.UNDO.getId());
+ bars.setGlobalActionHandler(ActionFactory.UNDO.getId(), action);
+
+ action = mTextEditor.getAction(ActionFactory.REDO.getId());
+ bars.setGlobalActionHandler(ActionFactory.REDO.getId(), action);
+
+ bars.updateActionBars();
+ }
+ }
+
+ /**
+ * Selects the default active page.
+ * @param defaultPageId the id of the page to show. If <code>null</code> the editor attempts to
+ * find the default page in the properties of the {@link IResource} object being edited.
+ */
+ protected void selectDefaultPage(String defaultPageId) {
+ if (defaultPageId == null) {
+ if (getEditorInput() instanceof IFileEditorInput) {
+ IFile file = ((IFileEditorInput) getEditorInput()).getFile();
+
+ QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID,
+ getClass().getSimpleName() + PREF_CURRENT_PAGE);
+ String pageId;
+ try {
+ pageId = file.getPersistentProperty(qname);
+ if (pageId != null) {
+ defaultPageId = pageId;
+ }
+ } catch (CoreException e) {
+ // ignored
+ }
+ }
+ }
+
+ if (defaultPageId != null) {
+ try {
+ setActivePage(Integer.parseInt(defaultPageId));
+ } catch (Exception e) {
+ // We can get NumberFormatException from parseInt but also
+ // AssertionError from setActivePage when the index is out of bounds.
+ // Generally speaking we just want to ignore any exception and fall back on the
+ // first page rather than crash the editor load. Logging the error is enough.
+ AdtPlugin.log(e, "Selecting page '%s' in AndroidXmlEditor failed", defaultPageId);
+ }
+ }
+ }
+
+ /**
+ * Removes all the pages from the editor.
+ */
+ protected void removePages() {
+ int count = getPageCount();
+ for (int i = count - 1 ; i >= 0 ; i--) {
+ removePage(i);
+ }
+ }
+
+ /**
+ * Overrides the parent's setActivePage to be able to switch to the xml editor.
+ *
+ * If the special pageId TEXT_EDITOR_ID is given, switches to the mTextPageIndex page.
+ * This is needed because the editor doesn't actually derive from IFormPage and thus
+ * doesn't have the get-by-page-id method. In this case, the method returns null since
+ * IEditorPart does not implement IFormPage.
+ */
+ @Override
+ public IFormPage setActivePage(String pageId) {
+ if (pageId.equals(TEXT_EDITOR_ID)) {
+ super.setActivePage(mTextPageIndex);
+ return null;
+ } else {
+ return super.setActivePage(pageId);
+ }
+ }
+
+
+ /**
+ * 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.
+ *
+ * @see MultiPageEditorPart#pageChange(int)
+ */
+ @Override
+ protected void pageChange(int newPageIndex) {
+ super.pageChange(newPageIndex);
+
+ // Do not record page changes during creation of pages
+ if (mIsCreatingPage) {
+ return;
+ }
+
+ if (getEditorInput() instanceof IFileEditorInput) {
+ IFile file = ((IFileEditorInput) getEditorInput()).getFile();
+
+ QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID,
+ getClass().getSimpleName() + PREF_CURRENT_PAGE);
+ try {
+ file.setPersistentProperty(qname, Integer.toString(newPageIndex));
+ } catch (CoreException e) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Notifies this listener that some resource changes
+ * are happening, or have already happened.
+ *
+ * Closes all project files on project close.
+ * @see IResourceChangeListener
+ */
+ public void resourceChanged(final IResourceChangeEvent event) {
+ if (event.getType() == IResourceChangeEvent.PRE_CLOSE) {
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ IWorkbenchPage[] pages = getSite().getWorkbenchWindow()
+ .getPages();
+ for (int i = 0; i < pages.length; i++) {
+ if (((FileEditorInput)mTextEditor.getEditorInput())
+ .getFile().getProject().equals(
+ event.getResource())) {
+ IEditorPart editorPart = pages[i].findEditor(mTextEditor
+ .getEditorInput());
+ pages[i].closeEditor(editorPart, true);
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Initializes the editor part with a site and input.
+ * <p/>
+ * Checks that the input is an instance of {@link IFileEditorInput}.
+ *
+ * @see FormEditor
+ */
+ @Override
+ public void init(IEditorSite site, IEditorInput editorInput) throws PartInitException {
+ if (!(editorInput instanceof IFileEditorInput))
+ throw new PartInitException("Invalid Input: Must be IFileEditorInput");
+ super.init(site, editorInput);
+ }
+
+ /**
+ * Removes attached listeners.
+ *
+ * @see WorkbenchPart
+ */
+ @Override
+ public void dispose() {
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+
+ super.dispose();
+ }
+
+ /**
+ * Commit all dirty pages then saves the contents of the text editor.
+ * <p/>
+ * This works by committing all data to the XML model and then
+ * asking the Structured XML Editor to save the XML.
+ *
+ * @see IEditorPart
+ */
+ @Override
+ public void doSave(IProgressMonitor monitor) {
+ commitPages(true /* onSave */);
+
+ // The actual "save" operation is done by the Structured XML Editor
+ getEditor(mTextPageIndex).doSave(monitor);
+ }
+
+ /* (non-Javadoc)
+ * Saves the contents of this editor to another object.
+ * <p>
+ * Subclasses must override this method to implement the open-save-close lifecycle
+ * for an editor. For greater details, see <code>IEditorPart</code>
+ * </p>
+ *
+ * @see IEditorPart
+ */
+ @Override
+ public void doSaveAs() {
+ commitPages(true /* onSave */);
+
+ IEditorPart editor = getEditor(mTextPageIndex);
+ editor.doSaveAs();
+ setPageText(mTextPageIndex, editor.getTitle());
+ setInput(editor.getEditorInput());
+ }
+
+ /**
+ * Commits all dirty pages in the editor. This method should
+ * be called as a first step of a 'save' operation.
+ * <p/>
+ * This is the same implementation as in {@link FormEditor}
+ * except it fixes two bugs: a cast to IFormPage is done
+ * from page.get(i) <em>before</em> being tested with instanceof.
+ * Another bug is that the last page might be a null pointer.
+ * <p/>
+ * The incorrect casting makes the original implementation crash due
+ * to our {@link StructuredTextEditor} not being an {@link IFormPage}
+ * so we have to override and duplicate to fix it.
+ *
+ * @param onSave <code>true</code> if commit is performed as part
+ * of the 'save' operation, <code>false</code> otherwise.
+ * @since 3.3
+ */
+ @Override
+ public void commitPages(boolean onSave) {
+ if (pages != null) {
+ for (int i = 0; i < pages.size(); i++) {
+ Object page = pages.get(i);
+ if (page != null && page instanceof IFormPage) {
+ IFormPage form_page = (IFormPage) page;
+ IManagedForm managed_form = form_page.getManagedForm();
+ if (managed_form != null && managed_form.isDirty()) {
+ managed_form.commit(onSave);
+ }
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * Returns whether the "save as" operation is supported by this editor.
+ * <p>
+ * Subclasses must override this method to implement the open-save-close lifecycle
+ * for an editor. For greater details, see <code>IEditorPart</code>
+ * </p>
+ *
+ * @see IEditorPart
+ */
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ // ---- Local methods ----
+
+
+ /**
+ * Helper method that creates a new hyper-link Listener.
+ * Used by derived classes which need active links in {@link FormText}.
+ * <p/>
+ * This link listener handles two kinds of URLs:
+ * <ul>
+ * <li> Links starting with "http" are simply sent to a local browser.
+ * <li> Links starting with "file:/" are simply sent to a local browser.
+ * <li> Links starting with "page:" are expected to be an editor page id to switch to.
+ * <li> Other links are ignored.
+ * </ul>
+ *
+ * @return A new hyper-link listener for FormText to use.
+ */
+ public final IHyperlinkListener createHyperlinkListener() {
+ return new HyperlinkAdapter() {
+ /**
+ * Switch to the page corresponding to the link that has just been clicked.
+ * For this purpose, the HREF of the &lt;a&gt; tags above is the page ID to switch to.
+ */
+ @Override
+ public void linkActivated(HyperlinkEvent e) {
+ super.linkActivated(e);
+ String link = e.data.toString();
+ if (link.startsWith("http") || //$NON-NLS-1$
+ link.startsWith("file:/")) { //$NON-NLS-1$
+ openLinkInBrowser(link);
+ } else if (link.startsWith("page:")) { //$NON-NLS-1$
+ // Switch to an internal page
+ setActivePage(link.substring(5 /* strlen("page:") */));
+ }
+ }
+ };
+ }
+
+ /**
+ * Open the http link into a browser
+ *
+ * @param link The URL to open in a browser
+ */
+ private void openLinkInBrowser(String link) {
+ try {
+ IWorkbenchBrowserSupport wbs = WorkbenchBrowserSupport.getInstance();
+ wbs.createBrowser(BROWSER_ID).openURL(new URL(link));
+ } catch (PartInitException e1) {
+ // pass
+ } catch (MalformedURLException e1) {
+ // pass
+ }
+ }
+
+ /**
+ * Creates the XML source editor.
+ * <p/>
+ * Memorizes the index page of the source editor (it's always the last page, but the number
+ * of pages before can change.)
+ * <br/>
+ * Retrieves the underlying XML model from the StructuredEditor and attaches a listener to it.
+ * Finally triggers modelChanged() on the model listener -- derived classes can use this
+ * to initialize the model the first time.
+ * <p/>
+ * Called only once <em>after</em> createFormPages.
+ */
+ private void createTextEditor() {
+ try {
+ mTextEditor = new TextEditor();
+ int index = addPage(mTextEditor, getEditorInput());
+ mTextPageIndex = index;
+ setPageText(index, mTextEditor.getTitle());
+
+ IDocumentProvider provider = mTextEditor.getDocumentProvider();
+ mDocument = provider.getDocument(getEditorInput());
+
+ mDocument.addDocumentListener(new IDocumentListener() {
+ public void documentChanged(DocumentEvent event) {
+ onDocumentChanged(event);
+ }
+
+ public void documentAboutToBeChanged(DocumentEvent event) {
+ // ignore
+ }
+ });
+
+
+ } catch (PartInitException e) {
+ ErrorDialog.openError(getSite().getShell(),
+ "Android Text Editor Error", null, e.getStatus());
+ }
+ }
+
+ /**
+ * Gives access to the {@link IDocument} from the {@link TextEditor}, corresponding to
+ * the current file input.
+ * <p/>
+ * All edits should be wrapped in a {@link #wrapRewriteSession(Runnable)}.
+ * The actual document instance is a {@link SynchronizableDocument}, which creates a lock
+ * around read/set operations. The base API provided by {@link IDocument} provides ways to
+ * manipulate the document line per line or as a bulk.
+ */
+ public IDocument getDocument() {
+ return mDocument;
+ }
+
+ /**
+ * Returns the {@link IProject} for the edited file.
+ */
+ public IProject getProject() {
+ if (mTextEditor != null) {
+ IEditorInput input = mTextEditor.getEditorInput();
+ if (input instanceof FileEditorInput) {
+ FileEditorInput fileInput = (FileEditorInput)input;
+ IFile inputFile = fileInput.getFile();
+
+ if (inputFile != null) {
+ return inputFile.getProject();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Runs the given operation in the context of a document RewriteSession.
+ * Takes care of properly starting and stopping the operation.
+ * <p/>
+ * The operation itself should just access {@link #getDocument()} and use the
+ * normal document's API to manipulate it.
+ *
+ * @see #getDocument()
+ */
+ public void wrapRewriteSession(Runnable operation) {
+ if (mDocument instanceof IDocumentExtension4) {
+ IDocumentExtension4 doc4 = (IDocumentExtension4) mDocument;
+
+ DocumentRewriteSession session = null;
+ try {
+ session = doc4.startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED_SMALL);
+
+ operation.run();
+ } catch(IllegalStateException e) {
+ AdtPlugin.log(e, "wrapRewriteSession failed");
+ e.printStackTrace();
+ } finally {
+ if (session != null) {
+ doc4.stopRewriteSession(session);
+ }
+ }
+
+ } else {
+ // Not an IDocumentExtension4? Unlikely. Try the operation anyway.
+ operation.run();
+ }
+ }
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/AbstractPropertiesFieldsPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/AbstractPropertiesFieldsPart.java
new file mode 100755
index 0000000..06169d2
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/AbstractPropertiesFieldsPart.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2010 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.export;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper.ManifestSectionPart;
+import com.android.sdklib.SdkConstants;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Section part for editing fields of a properties file in an Export editor.
+ * <p/>
+ * This base class is intended to be derived and customized.
+ */
+abstract class AbstractPropertiesFieldsPart extends ManifestSectionPart {
+
+ private final HashMap<String, Control> mNameToField = new HashMap<String, Control>();
+
+ private ExportEditor mEditor;
+
+ private boolean mInternalTextUpdate = false;
+
+ public AbstractPropertiesFieldsPart(Composite body, FormToolkit toolkit, ExportEditor editor) {
+ super(body, toolkit, Section.TWISTIE | Section.EXPANDED, true /* description */);
+ mEditor = editor;
+ }
+
+ protected HashMap<String, Control> getNameToField() {
+ return mNameToField;
+ }
+
+ protected ExportEditor getEditor() {
+ return mEditor;
+ }
+
+ protected void setInternalTextUpdate(boolean internalTextUpdate) {
+ mInternalTextUpdate = internalTextUpdate;
+ }
+
+ protected boolean isInternalTextUpdate() {
+ return mInternalTextUpdate;
+ }
+
+ /**
+ * Adds a modify listener to every text field that will mark the part as dirty.
+ *
+ * CONTRACT: Derived classes MUST call this at the end of their constructor.
+ *
+ * @see #setFieldModifyListener(Control, ModifyListener)
+ */
+ protected void addModifyListenerToFields() {
+ ModifyListener markDirtyListener = new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ // Mark the part as dirty if a field has been changed.
+ // This will force a commit() operation to store the data in the model.
+ if (!mInternalTextUpdate) {
+ markDirty();
+ }
+ }
+ };
+
+ for (Control field : mNameToField.values()) {
+ setFieldModifyListener(field, markDirtyListener);
+ }
+ }
+
+ /**
+ * Sets a listener that will mark the part as dirty when the control is modified.
+ * The base method only handles {@link Text} fields.
+ *
+ * CONTRACT: Derived classes CAN use this to add a listener to their own controls.
+ * The listener must call {@link #markDirty()} when the control is modified by the user.
+ *
+ * @param field A control previously registered with {@link #getNameToField()}.
+ * @param markDirtyListener A {@link ModifyListener} that invokes {@link #markDirty()}.
+ *
+ * @see #isInternalTextUpdate()
+ */
+ protected void setFieldModifyListener(Control field, ModifyListener markDirtyListener) {
+ if (field instanceof Text) {
+ ((Text) field).addModifyListener(markDirtyListener);
+ }
+ }
+
+ /**
+ * Updates the model based on the content of fields. This is invoked when a field
+ * has marked the document as dirty.
+ *
+ * CONTRACT: Derived classes do not need to override this.
+ */
+ @Override
+ public void commit(boolean onSave) {
+
+ // We didn't store any information indicating which field was dirty (we could).
+ // Since there are not many fields, just update all the document lines that
+ // match our field keywords.
+
+ if (isDirty()) {
+ mEditor.wrapRewriteSession(new Runnable() {
+ public void run() {
+ saveFieldsToModel();
+ }
+ });
+ }
+
+ super.commit(onSave);
+ }
+
+ private void saveFieldsToModel() {
+ // Get a list of all keywords to process. Go thru the document, replacing in-place
+ // the ones we can find and remove them from this set. This will leave the list
+ // of new keywords to add at the end of the document.
+ HashSet<String> allKeywords = new HashSet<String>(mNameToField.keySet());
+
+ IDocument doc = mEditor.getDocument();
+ int numLines = doc.getNumberOfLines();
+
+ String delim = null;
+ try {
+ delim = numLines > 0 ? doc.getLineDelimiter(0) : null;
+ } catch (BadLocationException e1) {
+ // ignore
+ }
+ if (delim == null || delim.length() == 0) {
+ delim = SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS ?
+ "\r\n" : "\n"; //$NON-NLS-1$ //$NON-NLS-2#
+ }
+
+ for (int i = 0; i < numLines; i++) {
+ try {
+ IRegion info = doc.getLineInformation(i);
+ String line = doc.get(info.getOffset(), info.getLength());
+ line = line.trim();
+ if (line.startsWith("#")) { //$NON-NLS-1$
+ continue;
+ }
+
+ int pos = line.indexOf('=');
+ if (pos > 0 && pos < line.length() - 1) {
+ String key = line.substring(0, pos).trim();
+
+ Control field = mNameToField.get(key);
+ if (field != null) {
+
+ // This is the new line to inject
+ line = key + "=" + getFieldText(field);
+
+ try {
+ // replace old line by new one. This doesn't change the
+ // line delimiter.
+ mInternalTextUpdate = true;
+ doc.replace(info.getOffset(), info.getLength(), line);
+ allKeywords.remove(key);
+ } finally {
+ mInternalTextUpdate = false;
+ }
+ }
+ }
+
+ } catch (BadLocationException e) {
+ // TODO log it
+ AdtPlugin.log(e, "Failed to replace in export.properties");
+ }
+ }
+
+ for (String key : allKeywords) {
+ Control field = mNameToField.get(key);
+ if (field != null) {
+ // This is the new line to inject
+ String line = key + "=" + getFieldText(field);
+
+ try {
+ // replace old line by new one
+ mInternalTextUpdate = true;
+
+ numLines = doc.getNumberOfLines();
+
+ IRegion info = numLines > 0 ? doc.getLineInformation(numLines - 1) : null;
+ if (info.getLength() == 0) {
+ // last line is empty. Insert right before there.
+ doc.replace(info.getOffset(), info.getLength(), line);
+ } else {
+ if (numLines > 0) {
+ String eofDelim = doc.getLineDelimiter(numLines - 1);
+ if (eofDelim == null || eofDelim.length() == 0) {
+ // The document doesn't end with a line delimiter, so add
+ // one to the line to be written.
+ line = delim + line;
+ }
+ }
+
+ int len = doc.getLength();
+ doc.replace(len, 0, line);
+ }
+
+ allKeywords.remove(key);
+ } catch (BadLocationException e) {
+ // TODO log it
+ AdtPlugin.log(e, "Failed to append to export.properties: %s", line);
+ } finally {
+ mInternalTextUpdate = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Used when committing fields values to the model to retrieve the text
+ * associated with a field.
+ * <p/>
+ * The base method only handles {@link Text} controls.
+ *
+ * CONTRACT: Derived classes CAN use this to support their own controls.
+ *
+ * @param field A control previously registered with {@link #getNameToField()}.
+ * @return A non-null string to write to the properties files.
+ */
+ protected String getFieldText(Control field) {
+ if (field instanceof Text) {
+ return ((Text) field).getText();
+ }
+ return "";
+ }
+
+ /**
+ * Called after all pages have been created, to let the parts initialize their
+ * content based on the document's model.
+ * <p/>
+ * The model should be acceded via the {@link ExportEditor}.
+ *
+ * @param editor The {@link ExportEditor} instance.
+ */
+ public void onModelInit(ExportEditor editor) {
+
+ // Start with a set of all the possible keywords and remove those we
+ // found in the document as we read the lines.
+ HashSet<String> allKeywords = new HashSet<String>(mNameToField.keySet());
+
+ // Parse the lines in the document for patterns "keyword=value",
+ // trimming all whitespace and discarding lines that start with # (comments)
+ // then affect to the internal fields as appropriate.
+ IDocument doc = editor.getDocument();
+ int numLines = doc.getNumberOfLines();
+ for (int i = 0; i < numLines; i++) {
+ try {
+ IRegion info = doc.getLineInformation(i);
+ String line = doc.get(info.getOffset(), info.getLength());
+ line = line.trim();
+ if (line.startsWith("#")) { //$NON-NLS-1$
+ continue;
+ }
+
+ int pos = line.indexOf('=');
+ if (pos > 0 && pos < line.length() - 1) {
+ String key = line.substring(0, pos).trim();
+
+ Control field = mNameToField.get(key);
+ if (field != null) {
+ String value = line.substring(pos + 1).trim();
+ try {
+ mInternalTextUpdate = true;
+ setFieldText(field, value);
+ allKeywords.remove(key);
+ } finally {
+ mInternalTextUpdate = false;
+ }
+ }
+ }
+
+ } catch (BadLocationException e) {
+ // TODO log it
+ AdtPlugin.log(e, "Failed to set field to export.properties value");
+ }
+ }
+
+ // Clear the text of any keyword we didn't find in the document
+ for (String key : allKeywords) {
+ Control field = mNameToField.get(key);
+ if (field != null) {
+ try {
+ mInternalTextUpdate = true;
+ setFieldText(field, "");
+ allKeywords.remove(key);
+ } finally {
+ mInternalTextUpdate = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Used when reading the model to set the field values.
+ * <p/>
+ * The base method only handles {@link Text} controls.
+ *
+ * CONTRACT: Derived classes CAN use this to support their own controls.
+ *
+ * @param field A control previously registered with {@link #getNameToField()}.
+ * @param value A non-null string to that was read from the properties files.
+ * The value is an empty string if the property line is missing.
+ */
+ protected void setFieldText(Control field, String value) {
+ if (field instanceof Text) {
+ ((Text) field).setText(value);
+ }
+ }
+
+ /**
+ * Called after the document model has been changed. The model should be acceded via
+ * the {@link ExportEditor} (e.g. getDocument, wrapRewriteSession)
+ *
+ * @param editor The {@link ExportEditor} instance.
+ * @param event Specification of changes applied to document.
+ */
+ public void onModelChanged(ExportEditor editor, DocumentEvent event) {
+ // To simplify and since we don't have many fields, just reload all the values.
+ // A better way would to be to look at DocumentEvent which gives us the offset/length
+ // and text that has changed.
+ if (!mInternalTextUpdate) {
+ onModelInit(editor);
+ }
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportEditor.java
new file mode 100755
index 0000000..50d9fd8
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportEditor.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2010 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.export;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.internal.editors.AndroidTextEditor;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.part.FileEditorInput;
+
+/**
+ * Multi-page form editor for export.properties in Export Projects.
+ */
+public class ExportEditor extends AndroidTextEditor {
+
+ public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".text.ExportEditor"; //$NON-NLS-1$
+
+ private ExportPropertiesPage mExportPropsPage;
+
+ /**
+ * Creates the form editor for resources XML files.
+ */
+ public ExportEditor() {
+ super();
+ }
+
+ // ---- Base Class Overrides ----
+
+ /**
+ * 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 true;
+ }
+
+ /**
+ * Create the various form pages.
+ */
+ @Override
+ protected void createFormPages() {
+ try {
+ mExportPropsPage = new ExportPropertiesPage(this);
+ addPage(mExportPropsPage);
+ } catch (PartInitException e) {
+ AdtPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$
+ }
+
+ }
+
+ /* (non-java doc)
+ * Change the tab/title name to include the project name.
+ */
+ @Override
+ protected void setInput(IEditorInput input) {
+ super.setInput(input);
+ if (input instanceof FileEditorInput) {
+ FileEditorInput fileInput = (FileEditorInput) input;
+ IFile file = fileInput.getFile();
+ setPartName(String.format("%1$s", file.getName()));
+ }
+ }
+
+ @Override
+ protected void postCreatePages() {
+ super.postCreatePages();
+ mExportPropsPage.onModelInit();
+ }
+
+ /**
+ * Indicates changes were made to the document.
+ *
+ * @param event Specification of changes applied to document.
+ */
+ @Override
+ protected void onDocumentChanged(DocumentEvent event) {
+ super.onDocumentChanged(event);
+ mExportPropsPage.onModelChanged(event);
+ }
+
+ // ---- Local Methods ----
+
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportFieldsPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportFieldsPart.java
new file mode 100755
index 0000000..eff3e48
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportFieldsPart.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 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.export;
+
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+
+import java.util.HashMap;
+
+/**
+ * Section part for editing the properties in an Export editor.
+ */
+final class ExportFieldsPart extends AbstractPropertiesFieldsPart {
+
+ public ExportFieldsPart(Composite body, FormToolkit toolkit, ExportEditor editor) {
+ super(body, toolkit, editor);
+ Section section = getSection();
+
+ section.setText("Export Properties");
+ section.setDescription("Properties of export.properties:");
+
+ Composite table = createTableLayout(toolkit, 2 /* numColumns */);
+
+ createLabel(table, toolkit,
+ "Available Properties", //label
+ "List of properties you can edit in export.properties"); //tooltip
+
+ Text packageField = createLabelAndText(table, toolkit,
+ "Package", //label,
+ "", //$NON-NLS-1$ value,
+ "TODO tooltip for Package"); //tooltip
+
+ Text projectsField = createLabelAndText(table, toolkit,
+ "Projects", //label,
+ "", //$NON-NLS-1$ value,
+ "TODO tooltip for Projects"); //tooltip
+
+ Text versionCodeField = createLabelAndText(table, toolkit,
+ "Version Code", //label,
+ "", //$NON-NLS-1$ value,
+ "TODO tooltip for Version Code"); //tooltip
+
+ Text keyStoreField = createLabelAndText(table, toolkit,
+ "Key Store", //label,
+ "", //$NON-NLS-1$ value,
+ "TODO tooltip for Key Store"); //tooltip
+
+ Text keyAliasField = createLabelAndText(table, toolkit,
+ "Key Alias", //label,
+ "", //$NON-NLS-1$ value,
+ "TODO tooltip for Key Alias"); //tooltip
+
+ // Associate each field with the keyword in the properties files.
+ // TODO there's probably some constant to reuse here.
+ HashMap<String, Control> map = getNameToField();
+ map.put("package", packageField); //$NON-NLS-1$
+ map.put("projects", projectsField); //$NON-NLS-1$
+ map.put("versionCode", versionCodeField); //$NON-NLS-1$
+ map.put("_key.store", keyStoreField); //$NON-NLS-1$
+ map.put("_key.alias", keyAliasField); //$NON-NLS-1$
+
+ addModifyListenerToFields();
+ }
+
+ @Override
+ protected void setFieldModifyListener(Control field, ModifyListener markDirtyListener) {
+ super.setFieldModifyListener(field, markDirtyListener);
+ // TODO override for custom controls
+ }
+
+ @Override
+ protected String getFieldText(Control field) {
+ // TODO override for custom controls
+ return super.getFieldText(field);
+ }
+
+ @Override
+ protected void setFieldText(Control field, String value) {
+ // TODO override for custom controls
+ super.setFieldText(field, value);
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportLinksPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportLinksPart.java
new file mode 100755
index 0000000..31ea988
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportLinksPart.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 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.export;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper.ManifestSectionPart;
+
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.events.IHyperlinkListener;
+import org.eclipse.ui.forms.widgets.FormText;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+
+/**
+ * Links section part for export properties page.
+ * Displays some help and some links/actions for the user to use.
+ */
+final class ExportLinksPart extends ManifestSectionPart {
+
+ private FormText mFormText;
+
+ public ExportLinksPart(Composite body, FormToolkit toolkit, ExportEditor editor) {
+ super(body, toolkit, Section.TWISTIE | Section.EXPANDED, true /* description */);
+ Section section = getSection();
+ section.setText("Links");
+ section.setDescription("TODO SOME TEXT HERE. You can also edit the XML directly.");
+
+ final Composite table = createTableLayout(toolkit, 2 /* numColumns */);
+
+ StringBuffer buf = new StringBuffer();
+ buf.append("<form>"); // $NON-NLS-1$
+
+ buf.append("<li style=\"image\" value=\"android_img\"><a href=\"action_dosomething\">");
+ buf.append("TODO Custom Action");
+ buf.append("</a>"); //$NON-NLS-1$
+ buf.append(": blah blah do something (like build/export).");
+ buf.append("</li>"); //$NON-NLS-1$
+
+ buf.append(String.format("<li style=\"image\" value=\"android_img\"><a href=\"page:%1$s\">", // $NON-NLS-1$
+ ExportEditor.TEXT_EDITOR_ID));
+ buf.append("XML Source");
+ buf.append("</a>"); //$NON-NLS-1$
+ buf.append(": Directly edit the AndroidManifest.xml file.");
+ buf.append("</li>"); //$NON-NLS-1$
+
+ buf.append("<li style=\"image\" value=\"android_img\">"); // $NON-NLS-1$
+ buf.append("<a href=\"http://code.google.com/android/devel/bblocks-manifest.html\">Documentation</a>: Documentation from the Android SDK for AndroidManifest.xml."); // $NON-NLS-1$
+ buf.append("</li>"); //$NON-NLS-1$
+ buf.append("</form>"); //$NON-NLS-1$
+
+ mFormText = createFormText(table, toolkit, true, buf.toString(),
+ false /* setupLayoutData */);
+
+ Image androidLogo = AdtPlugin.getAndroidLogo();
+ mFormText.setImage("android_img", androidLogo); //$NON-NLS-1$
+
+ // Listener for default actions (page change, URL web browser)
+ mFormText.addHyperlinkListener(editor.createHyperlinkListener());
+
+ mFormText.addHyperlinkListener(new IHyperlinkListener() {
+ public void linkExited(HyperlinkEvent e) {
+ // pass
+ }
+
+ public void linkEntered(HyperlinkEvent e) {
+ // pass
+ }
+
+ public void linkActivated(HyperlinkEvent e) {
+ String link = e.data.toString();
+ if ("action_dosomething".equals(link)) {
+ MessageBox mb = new MessageBox(table.getShell(), SWT.OK);
+ mb.setText("Custom Action Invoked");
+ mb.open();
+ }
+ }
+ });
+ }
+
+ /**
+ * Called after all pages have been created, to let the parts initialize their
+ * content based on the document's model.
+ * <p/>
+ * The model should be acceded via the {@link ExportEditor}.
+ *
+ * @param editor The {@link ExportEditor} instance.
+ */
+ public void onModelInit(ExportEditor editor) {
+ // pass
+ }
+
+ /**
+ * Called after the document model has been changed. The model should be acceded via
+ * the {@link ExportEditor} (e.g. getDocument, wrapRewriteSession)
+ *
+ * @param editor The {@link ExportEditor} instance.
+ * @param event Specification of changes applied to document.
+ */
+ public void onModelChanged(ExportEditor editor, DocumentEvent event) {
+ // pass
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportPropertiesPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportPropertiesPage.java
new file mode 100755
index 0000000..f3db5ee
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/export/ExportPropertiesPage.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2010 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.export;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormPage;
+import org.eclipse.ui.forms.widgets.ColumnLayout;
+import org.eclipse.ui.forms.widgets.ColumnLayoutData;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+
+
+/**
+ * Page for export properties, used by {@link ExportEditor}.
+ * It displays a part to edit the properties and another part
+ * to provide some links and actions.
+ */
+public final class ExportPropertiesPage extends FormPage {
+
+ /** Page id used for switching tabs programmatically */
+ final static String PAGE_ID = "export_prop_page"; //$NON-NLS-1$
+
+ /** Container editor */
+ ExportEditor mEditor;
+ /** Export fields part */
+ private ExportFieldsPart mFieldsPart;
+ /** Export links part */
+ private ExportLinksPart mLinksPart;
+
+ public ExportPropertiesPage(ExportEditor editor) {
+ super(editor, PAGE_ID, "Export Properties"); // tab's label, user visible, keep it short
+ mEditor = editor;
+ }
+
+ /**
+ * Creates the content in the form hosted in this page.
+ *
+ * @param managedForm the form hosted in this page.
+ */
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ super.createFormContent(managedForm);
+ ScrolledForm form = managedForm.getForm();
+ form.setText("Android Export Properties");
+ form.setImage(AdtPlugin.getAndroidLogo());
+
+ Composite body = form.getBody();
+ FormToolkit toolkit = managedForm.getToolkit();
+
+ body.setLayout(new ColumnLayout());
+
+ mFieldsPart = new ExportFieldsPart(body, toolkit, mEditor);
+ mFieldsPart.getSection().setLayoutData(new ColumnLayoutData());
+ managedForm.addPart(mFieldsPart);
+
+ mLinksPart = new ExportLinksPart(body, toolkit, mEditor);
+ mLinksPart.getSection().setLayoutData(new ColumnLayoutData());
+ managedForm.addPart(mLinksPart);
+
+ mFieldsPart.onModelInit(mEditor);
+ mLinksPart.onModelInit(mEditor);
+ }
+
+ /**
+ * Called after all pages have been created, to let the parts initialize their
+ * content based on the document's model.
+ * <p/>
+ * The model should be acceded via the {@link ExportEditor}.
+ */
+ public void onModelInit() {
+ if (mFieldsPart != null) {
+ mFieldsPart.onModelInit(mEditor);
+ }
+
+ if (mLinksPart != null) {
+ mLinksPart.onModelInit(mEditor);
+ }
+ }
+
+ /**
+ * Called after the document model has been changed. The model should be acceded via
+ * the {@link ExportEditor}.
+ *
+ * @param event Specification of changes applied to document.
+ */
+ public void onModelChanged(DocumentEvent event) {
+ if (mFieldsPart != null) {
+ mFieldsPart.onModelChanged(mEditor, event);
+ }
+
+ if (mLinksPart != null) {
+ mLinksPart.onModelChanged(mEditor, event);
+ }
+ }
+}