1b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll/*
2b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * Copyright (C) 2010 The Android Open Source Project
3b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll *
4b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * you may not use this file except in compliance with the License.
6b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * You may obtain a copy of the License at
7b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll *
8b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll *      http://www.eclipse.org/org/documents/epl-v10.php
9b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll *
10b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * Unless required by applicable law or agreed to in writing, software
11b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * distributed under the License is distributed on an "AS IS" BASIS,
12b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * See the License for the specific language governing permissions and
14b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * limitations under the License.
15b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll */
16b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
17b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollpackage com.android.ide.eclipse.adt.internal.editors;
18b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
19b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport com.android.ide.eclipse.adt.AdtPlugin;
20b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
21b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.core.internal.filebuffers.SynchronizableDocument;
22b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.core.resources.IFile;
23b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.core.resources.IProject;
24b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.core.resources.IResource;
25b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.core.resources.IResourceChangeEvent;
26b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.core.resources.IResourceChangeListener;
27b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.core.resources.ResourcesPlugin;
28b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.core.runtime.CoreException;
29b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.core.runtime.IProgressMonitor;
30b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.core.runtime.QualifiedName;
31b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.jface.action.IAction;
32b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.jface.dialogs.ErrorDialog;
33b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.jface.text.DocumentEvent;
34b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.jface.text.DocumentRewriteSession;
35b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.jface.text.DocumentRewriteSessionType;
36b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.jface.text.IDocument;
37b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.jface.text.IDocumentExtension4;
38b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.jface.text.IDocumentListener;
39b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.swt.widgets.Display;
40b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.IActionBars;
41b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.IEditorInput;
42b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.IEditorPart;
43b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.IEditorSite;
44b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.IFileEditorInput;
45b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.IWorkbenchPage;
46b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.PartInitException;
47b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.actions.ActionFactory;
48b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.browser.IWorkbenchBrowserSupport;
49b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.editors.text.TextEditor;
50b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.forms.IManagedForm;
51b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.forms.editor.FormEditor;
52b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.forms.editor.IFormPage;
53b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.forms.events.HyperlinkAdapter;
54b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.forms.events.HyperlinkEvent;
55b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.forms.events.IHyperlinkListener;
56b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.forms.widgets.FormText;
57b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.internal.browser.WorkbenchBrowserSupport;
58b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.part.FileEditorInput;
59b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.part.MultiPageEditorPart;
60b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.part.WorkbenchPart;
61b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.ui.texteditor.IDocumentProvider;
62b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport org.eclipse.wst.sse.ui.StructuredTextEditor;
63b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
64b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport java.net.MalformedURLException;
65b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollimport java.net.URL;
66b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
67b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll/**
68b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * Multi-page form editor for Android text files.
69b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * <p/>
70b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * It is designed to work with a {@link TextEditor} that will display a text file.
71b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * <br/>
72b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * Derived classes must implement createFormPages to create the forms before the
73b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll * source editor. This can be a no-op if desired.
74b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll */
754c07263da057b6014342089097a3a4c6ebe993d2Raphael@SuppressWarnings("restriction")
76b9dd664f2996e4090603e3fc224b98453a49f759Raphael Mollpublic abstract class AndroidTextEditor extends FormEditor implements IResourceChangeListener {
77b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
78b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /** Preference name for the current page of this file */
79b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    private static final String PREF_CURRENT_PAGE = "_current_page";
80b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
81b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /** Id string used to create the Android SDK browser */
8223da069e4f407df1b06e7db2324e3247496abe3dTor Norbye    private static String BROWSER_ID = "android"; //$NON-NLS-1$
83b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
84b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /** Page id of the XML source editor, used for switching tabs programmatically */
85b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public final static String TEXT_EDITOR_ID = "editor_part"; //$NON-NLS-1$
86b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
87b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /** Width hint for text fields. Helps the grid layout resize properly on smaller screens */
88b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public static final int TEXT_WIDTH_HINT = 50;
89b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
90b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /** Page index of the text editor (always the last page) */
91b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    private int mTextPageIndex;
92b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
93b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /** The text editor */
94b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    private TextEditor mTextEditor;
95b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
96b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /** flag set during page creation */
97b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    private boolean mIsCreatingPage = false;
98b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
99b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    private IDocument mDocument;
100b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
101b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
102b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Creates a form editor.
103b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
104b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public AndroidTextEditor() {
105b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        super();
106b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
107b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
108b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    // ---- Abstract Methods ----
109b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
110b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
111b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Creates the various form pages.
112b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p/>
113b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Derived classes must implement this to add their own specific tabs.
114b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
115b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    abstract protected void createFormPages();
116b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
117b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
118b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Called by the base class {@link AndroidTextEditor} once all pages (custom form pages
119b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * as well as text editor page) have been created. This give a chance to deriving
120b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * classes to adjust behavior once the text page has been created.
121b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
122b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    protected void postCreatePages() {
123b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        // Nothing in the base class.
124b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
125b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
126b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
127b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Subclasses should override this method to process the new text model.
128b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * This is called after the document has been edited.
129b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
130b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * The base implementation is empty.
131b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
132b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @param event Specification of changes applied to document.
133b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
134b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    protected void onDocumentChanged(DocumentEvent event) {
135b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        // pass
136b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
137b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
138b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    // ---- Base Class Overrides, Interfaces Implemented ----
139b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
140b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
141b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Creates the pages of the multi-page editor.
142b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
143b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    @Override
144b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    protected void addPages() {
145b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        createAndroidPages();
146b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        selectDefaultPage(null /* defaultPageId */);
147b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
148b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
149b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
150b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Creates the page for the Android Editors
151b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
152b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    protected void createAndroidPages() {
153b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        mIsCreatingPage = true;
154b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        createFormPages();
155b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        createTextEditor();
156b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        createUndoRedoActions();
157b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        postCreatePages();
158b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        mIsCreatingPage = false;
159b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
160b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
161b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
162b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Returns whether the editor is currently creating its pages.
163b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
164b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public boolean isCreatingPages() {
165b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        return mIsCreatingPage;
166b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
167b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
168b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
169b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Creates undo redo actions for the editor site (so that it works for any page of this
170b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * multi-page editor) by re-using the actions defined by the {@link TextEditor}
171b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * (aka the XML text editor.)
172b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
173b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    private void createUndoRedoActions() {
174b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        IActionBars bars = getEditorSite().getActionBars();
175b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (bars != null) {
176b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            IAction action = mTextEditor.getAction(ActionFactory.UNDO.getId());
177b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            bars.setGlobalActionHandler(ActionFactory.UNDO.getId(), action);
178b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
179b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            action = mTextEditor.getAction(ActionFactory.REDO.getId());
180b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            bars.setGlobalActionHandler(ActionFactory.REDO.getId(), action);
181b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
182b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            bars.updateActionBars();
183b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
184b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
185b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
186b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
187b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Selects the default active page.
188b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @param defaultPageId the id of the page to show. If <code>null</code> the editor attempts to
189b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * find the default page in the properties of the {@link IResource} object being edited.
190b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
191b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    protected void selectDefaultPage(String defaultPageId) {
192b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (defaultPageId == null) {
193b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            if (getEditorInput() instanceof IFileEditorInput) {
194b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                IFile file = ((IFileEditorInput) getEditorInput()).getFile();
195b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
196b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID,
197b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                        getClass().getSimpleName() + PREF_CURRENT_PAGE);
198b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                String pageId;
199b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                try {
200b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    pageId = file.getPersistentProperty(qname);
201b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    if (pageId != null) {
202b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                        defaultPageId = pageId;
203b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    }
204b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                } catch (CoreException e) {
205b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    // ignored
206b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                }
207b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            }
208b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
209b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
210b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (defaultPageId != null) {
211b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            try {
212b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                setActivePage(Integer.parseInt(defaultPageId));
213b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            } catch (Exception e) {
214b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                // We can get NumberFormatException from parseInt but also
215b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                // AssertionError from setActivePage when the index is out of bounds.
216b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                // Generally speaking we just want to ignore any exception and fall back on the
217b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                // first page rather than crash the editor load. Logging the error is enough.
218b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                AdtPlugin.log(e, "Selecting page '%s' in AndroidXmlEditor failed", defaultPageId);
219b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            }
220b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
221b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
222b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
223b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
224b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Removes all the pages from the editor.
225b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
226b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    protected void removePages() {
227b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        int count = getPageCount();
228b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        for (int i = count - 1 ; i >= 0 ; i--) {
229b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            removePage(i);
230b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
231b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
232b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
233b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
234b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Overrides the parent's setActivePage to be able to switch to the xml editor.
235b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
236b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * If the special pageId TEXT_EDITOR_ID is given, switches to the mTextPageIndex page.
237b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * This is needed because the editor doesn't actually derive from IFormPage and thus
238b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * doesn't have the get-by-page-id method. In this case, the method returns null since
239b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * IEditorPart does not implement IFormPage.
240b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
241b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    @Override
242b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public IFormPage setActivePage(String pageId) {
243b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (pageId.equals(TEXT_EDITOR_ID)) {
244b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            super.setActivePage(mTextPageIndex);
245b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            return null;
246b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        } else {
247b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            return super.setActivePage(pageId);
248b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
249b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
250b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
251b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
252b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
253b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Notifies this multi-page editor that the page with the given id has been
254b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * activated. This method is called when the user selects a different tab.
255b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
256b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @see MultiPageEditorPart#pageChange(int)
257b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
258b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    @Override
259b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    protected void pageChange(int newPageIndex) {
260b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        super.pageChange(newPageIndex);
261b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
262b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        // Do not record page changes during creation of pages
263b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (mIsCreatingPage) {
264b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            return;
265b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
266b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
267b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (getEditorInput() instanceof IFileEditorInput) {
268b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            IFile file = ((IFileEditorInput) getEditorInput()).getFile();
269b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
270b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            QualifiedName qname = new QualifiedName(AdtPlugin.PLUGIN_ID,
271b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    getClass().getSimpleName() + PREF_CURRENT_PAGE);
272b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            try {
273b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                file.setPersistentProperty(qname, Integer.toString(newPageIndex));
274b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            } catch (CoreException e) {
275b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                // ignore
276b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            }
277b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
278b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
279b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
280b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
281b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Notifies this listener that some resource changes
282b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * are happening, or have already happened.
283b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
284b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Closes all project files on project close.
285b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @see IResourceChangeListener
286b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
287ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye    @Override
288b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public void resourceChanged(final IResourceChangeEvent event) {
289b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (event.getType() == IResourceChangeEvent.PRE_CLOSE) {
290b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            Display.getDefault().asyncExec(new Runnable() {
291ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye                @Override
292b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                public void run() {
2934c07263da057b6014342089097a3a4c6ebe993d2Raphael                    @SuppressWarnings("hiding")
2944c07263da057b6014342089097a3a4c6ebe993d2Raphael                    IWorkbenchPage[] pages = getSite().getWorkbenchWindow().getPages();
295b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    for (int i = 0; i < pages.length; i++) {
296b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                        if (((FileEditorInput)mTextEditor.getEditorInput())
297b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                                .getFile().getProject().equals(
298b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                                        event.getResource())) {
299b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                            IEditorPart editorPart = pages[i].findEditor(mTextEditor
300b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                                    .getEditorInput());
301b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                            pages[i].closeEditor(editorPart, true);
302b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                        }
303b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    }
304b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                }
305b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            });
306b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
307b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
308b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
309b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
310b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Initializes the editor part with a site and input.
311b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p/>
312b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Checks that the input is an instance of {@link IFileEditorInput}.
313b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
314b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @see FormEditor
315b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
316b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    @Override
317b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public void init(IEditorSite site, IEditorInput editorInput) throws PartInitException {
318b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (!(editorInput instanceof IFileEditorInput))
319b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            throw new PartInitException("Invalid Input: Must be IFileEditorInput");
320b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        super.init(site, editorInput);
321b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
322b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
323b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
324aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll     * Returns the {@link IFile} matching the editor's input or null.
325aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll     * <p/>
326aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll     * By construction, the editor input has to be an {@link IFileEditorInput} so it must
327aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll     * have an associated {@link IFile}. Null can only be returned if this editor has no
328aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll     * input somehow.
329aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll     */
330aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll    public IFile getFile() {
331aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll        if (getEditorInput() instanceof IFileEditorInput) {
332aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll            return ((IFileEditorInput) getEditorInput()).getFile();
333aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll        }
334aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll        return null;
335aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll    }
336aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll
337aa80eb0797889e53c43ef91bb8e63d8d9d5f1d24Raphael Moll    /**
338b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Removes attached listeners.
339b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
340b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @see WorkbenchPart
341b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
342b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    @Override
343b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public void dispose() {
344b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
345b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
346b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        super.dispose();
347b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
348b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
349b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
350b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Commit all dirty pages then saves the contents of the text editor.
351b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p/>
352b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * This works by committing all data to the XML model and then
353b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * asking the Structured XML Editor to save the XML.
354b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
355b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @see IEditorPart
356b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
357b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    @Override
358b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public void doSave(IProgressMonitor monitor) {
359b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        commitPages(true /* onSave */);
360b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
361b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        // The actual "save" operation is done by the Structured XML Editor
362b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        getEditor(mTextPageIndex).doSave(monitor);
363b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
364b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
365b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /* (non-Javadoc)
366b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Saves the contents of this editor to another object.
367b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p>
368b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Subclasses must override this method to implement the open-save-close lifecycle
369b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * for an editor.  For greater details, see <code>IEditorPart</code>
370b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * </p>
371b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
372b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @see IEditorPart
373b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
374b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    @Override
375b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public void doSaveAs() {
376b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        commitPages(true /* onSave */);
377b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
378b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        IEditorPart editor = getEditor(mTextPageIndex);
379b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        editor.doSaveAs();
380b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        setPageText(mTextPageIndex, editor.getTitle());
381b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        setInput(editor.getEditorInput());
382b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
383b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
384b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
385b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Commits all dirty pages in the editor. This method should
386b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * be called as a first step of a 'save' operation.
387b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p/>
388b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * This is the same implementation as in {@link FormEditor}
389b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * except it fixes two bugs: a cast to IFormPage is done
390b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * from page.get(i) <em>before</em> being tested with instanceof.
391b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Another bug is that the last page might be a null pointer.
392b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p/>
393b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * The incorrect casting makes the original implementation crash due
394b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * to our {@link StructuredTextEditor} not being an {@link IFormPage}
395b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * so we have to override and duplicate to fix it.
396b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
397b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @param onSave <code>true</code> if commit is performed as part
398b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * of the 'save' operation, <code>false</code> otherwise.
399b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @since 3.3
400b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
401b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    @Override
402b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public void commitPages(boolean onSave) {
403b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (pages != null) {
404b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            for (int i = 0; i < pages.size(); i++) {
405b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                Object page = pages.get(i);
406b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                if (page != null && page instanceof IFormPage) {
407b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    IFormPage form_page = (IFormPage) page;
408b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    IManagedForm managed_form = form_page.getManagedForm();
409b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    if (managed_form != null && managed_form.isDirty()) {
410b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                        managed_form.commit(onSave);
411b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    }
412b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                }
413b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            }
414b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
415b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
416b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
417b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /* (non-Javadoc)
418b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Returns whether the "save as" operation is supported by this editor.
419b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p>
420b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Subclasses must override this method to implement the open-save-close lifecycle
421b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * for an editor.  For greater details, see <code>IEditorPart</code>
422b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * </p>
423b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
424b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @see IEditorPart
425b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
426b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    @Override
427b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public boolean isSaveAsAllowed() {
428b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        return false;
429b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
430b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
431b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    // ---- Local methods ----
432b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
433b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
434b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
435b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Helper method that creates a new hyper-link Listener.
436b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Used by derived classes which need active links in {@link FormText}.
437b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p/>
438b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * This link listener handles two kinds of URLs:
439b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <ul>
440b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <li> Links starting with "http" are simply sent to a local browser.
441b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <li> Links starting with "file:/" are simply sent to a local browser.
442b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <li> Links starting with "page:" are expected to be an editor page id to switch to.
443b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <li> Other links are ignored.
444b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * </ul>
445b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
446b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @return A new hyper-link listener for FormText to use.
447b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
448b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public final IHyperlinkListener createHyperlinkListener() {
449b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        return new HyperlinkAdapter() {
450b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            /**
451b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll             * Switch to the page corresponding to the link that has just been clicked.
452b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll             * For this purpose, the HREF of the &lt;a&gt; tags above is the page ID to switch to.
453b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll             */
454b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            @Override
455b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            public void linkActivated(HyperlinkEvent e) {
456b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                super.linkActivated(e);
457b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                String link = e.data.toString();
458b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                if (link.startsWith("http") ||          //$NON-NLS-1$
459b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                        link.startsWith("file:/")) {    //$NON-NLS-1$
460b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    openLinkInBrowser(link);
461b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                } else if (link.startsWith("page:")) {  //$NON-NLS-1$
462b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    // Switch to an internal page
463b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    setActivePage(link.substring(5 /* strlen("page:") */));
464b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                }
465b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            }
466b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        };
467b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
468b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
469b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
470b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Open the http link into a browser
471b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
472b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @param link The URL to open in a browser
473b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
474b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    private void openLinkInBrowser(String link) {
475b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        try {
476b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            IWorkbenchBrowserSupport wbs = WorkbenchBrowserSupport.getInstance();
477b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            wbs.createBrowser(BROWSER_ID).openURL(new URL(link));
478b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        } catch (PartInitException e1) {
479b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            // pass
480b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        } catch (MalformedURLException e1) {
481b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            // pass
482b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
483b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
484b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
485b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
486b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Creates the XML source editor.
487b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p/>
488b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Memorizes the index page of the source editor (it's always the last page, but the number
489b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * of pages before can change.)
490b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <br/>
491b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Retrieves the underlying XML model from the StructuredEditor and attaches a listener to it.
492b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Finally triggers modelChanged() on the model listener -- derived classes can use this
493b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * to initialize the model the first time.
494b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p/>
495b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Called only once <em>after</em> createFormPages.
496b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
497b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    private void createTextEditor() {
498b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        try {
499b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            mTextEditor = new TextEditor();
500b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            int index = addPage(mTextEditor, getEditorInput());
501b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            mTextPageIndex = index;
502b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            setPageText(index, mTextEditor.getTitle());
503b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
504b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            IDocumentProvider provider = mTextEditor.getDocumentProvider();
505b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            mDocument = provider.getDocument(getEditorInput());
506b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
507b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            mDocument.addDocumentListener(new IDocumentListener() {
508ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye                @Override
509b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                public void documentChanged(DocumentEvent event) {
510b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    onDocumentChanged(event);
511b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                }
512b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
513ab36f4e7488358dea4ab6b54ee2b7bef3da0232bTor Norbye                @Override
514b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                public void documentAboutToBeChanged(DocumentEvent event) {
515b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    // ignore
516b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                }
517b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            });
518b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
519b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
520b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        } catch (PartInitException e) {
521b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            ErrorDialog.openError(getSite().getShell(),
522b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    "Android Text Editor Error", null, e.getStatus());
523b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
524b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
525b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
526b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
527b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Gives access to the {@link IDocument} from the {@link TextEditor}, corresponding to
528b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * the current file input.
529b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p/>
530b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * All edits should be wrapped in a {@link #wrapRewriteSession(Runnable)}.
531b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * The actual document instance is a {@link SynchronizableDocument}, which creates a lock
532b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * around read/set operations. The base API provided by {@link IDocument} provides ways to
533b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * manipulate the document line per line or as a bulk.
534b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
535b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public IDocument getDocument() {
536b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        return mDocument;
537b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
538b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
539b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
540b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Returns the {@link IProject} for the edited file.
541b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
542b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public IProject getProject() {
543b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (mTextEditor != null) {
544b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            IEditorInput input = mTextEditor.getEditorInput();
545b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            if (input instanceof FileEditorInput) {
546b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                FileEditorInput fileInput = (FileEditorInput)input;
547b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                IFile inputFile = fileInput.getFile();
548b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
549b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                if (inputFile != null) {
550b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    return inputFile.getProject();
551b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                }
552b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            }
553b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
554b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
555b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        return null;
556b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
557b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
558b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    /**
559b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Runs the given operation in the context of a document RewriteSession.
560b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * Takes care of properly starting and stopping the operation.
561b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * <p/>
562b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * The operation itself should just access {@link #getDocument()} and use the
563b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * normal document's API to manipulate it.
564b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     *
565b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     * @see #getDocument()
566b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll     */
567b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    public void wrapRewriteSession(Runnable operation) {
568b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        if (mDocument instanceof IDocumentExtension4) {
569b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            IDocumentExtension4 doc4 = (IDocumentExtension4) mDocument;
570b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
571b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            DocumentRewriteSession session = null;
572b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            try {
573b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                session = doc4.startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED_SMALL);
574b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
575b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                operation.run();
576b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            } catch(IllegalStateException e) {
577b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                AdtPlugin.log(e, "wrapRewriteSession failed");
578b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                e.printStackTrace();
579b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            } finally {
580b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                if (session != null) {
581b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                    doc4.stopRewriteSession(session);
582b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll                }
583b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            }
584b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
585b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        } else {
586b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            // Not an IDocumentExtension4? Unlikely. Try the operation anyway.
587b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll            operation.run();
588b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll        }
589b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll    }
590b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll
591b9dd664f2996e4090603e3fc224b98453a49f759Raphael Moll}
592