OSDN Git Service

a59b32f7194d3e9d1d6e52bb1c45ac9f69da4490
[android-x86/sdk.git] / eclipse / plugins / com.android.ide.eclipse.adt / src / com / android / ide / eclipse / adt / internal / editors / layout / LayoutEditor.java
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.ide.eclipse.adt.internal.editors.layout;
18
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 import com.android.ide.eclipse.adt.AndroidConstants;
21 import com.android.ide.eclipse.adt.internal.editors.AndroidEditor;
22 import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
23 import com.android.ide.eclipse.adt.internal.editors.layout.gle1.GraphicalLayoutEditor;
24 import com.android.ide.eclipse.adt.internal.editors.layout.gle1.UiContentOutlinePage;
25 import com.android.ide.eclipse.adt.internal.editors.layout.gle1.UiPropertySheetPage;
26 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
27 import com.android.ide.eclipse.adt.internal.editors.ui.tree.UiActions;
28 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
29 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
30 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolder;
31 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
32 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
33 import com.android.ide.eclipse.adt.internal.ui.EclipseUiHelper;
34
35 import org.eclipse.core.resources.IFile;
36 import org.eclipse.core.runtime.IProgressMonitor;
37 import org.eclipse.core.runtime.NullProgressMonitor;
38 import org.eclipse.gef.ui.parts.TreeViewer;
39 import org.eclipse.ui.IEditorInput;
40 import org.eclipse.ui.IEditorPart;
41 import org.eclipse.ui.IPartListener;
42 import org.eclipse.ui.IShowEditorInput;
43 import org.eclipse.ui.IWorkbenchPage;
44 import org.eclipse.ui.IWorkbenchPart;
45 import org.eclipse.ui.IWorkbenchPartSite;
46 import org.eclipse.ui.PartInitException;
47 import org.eclipse.ui.part.FileEditorInput;
48 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
49 import org.eclipse.ui.views.properties.IPropertySheetPage;
50 import org.w3c.dom.Document;
51
52 /**
53  * Multi-page form editor for /res/layout XML files.
54  */
55 public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPartListener {
56
57     public static final String ID = AndroidConstants.EDITORS_NAMESPACE + ".layout.LayoutEditor"; //$NON-NLS-1$
58
59     /** Root node of the UI element hierarchy */
60     private UiDocumentNode mUiRootNode;
61
62     private IGraphicalLayoutEditor mGraphicalEditor;
63     private int mGraphicalEditorIndex;
64     /** Implementation of the {@link IContentOutlinePage} for this editor */
65     private UiContentOutlinePage mOutline;
66     /** Custom implementation of {@link IPropertySheetPage} for this editor */
67     private UiPropertySheetPage mPropertyPage;
68
69     private UiEditorActions mUiEditorActions;
70
71     /**
72      * Creates the form editor for resources XML files.
73      */
74     public LayoutEditor() {
75         super();
76     }
77
78     /**
79      * @return The root node of the UI element hierarchy
80      */
81     @Override
82     public UiDocumentNode getUiRootNode() {
83         return mUiRootNode;
84     }
85
86     // ---- Base Class Overrides ----
87
88     @Override
89     public void dispose() {
90         getSite().getPage().removePartListener(this);
91
92         super.dispose();
93     }
94
95     /**
96      * Save the XML.
97      * <p/>
98      * The actual save operation is done in the super class by committing
99      * all data to the XML model and then having the Structured XML Editor
100      * save the XML.
101      * <p/>
102      * Here we just need to tell the graphical editor that the model has
103      * been saved.
104      */
105     @Override
106     public void doSave(IProgressMonitor monitor) {
107         super.doSave(monitor);
108         if (mGraphicalEditor != null) {
109             mGraphicalEditor.doSave(monitor);
110         }
111     }
112
113     /**
114      * Returns whether the "save as" operation is supported by this editor.
115      * <p/>
116      * Save-As is a valid operation for the ManifestEditor since it acts on a
117      * single source file.
118      *
119      * @see IEditorPart
120      */
121     @Override
122     public boolean isSaveAsAllowed() {
123         return true;
124     }
125
126     /**
127      * Create the various form pages.
128      */
129     @Override
130     protected void createFormPages() {
131         try {
132             // The graphical layout editor is now enabled by default.
133             // In case there's an issue we provide a way to disable it using an
134             // env variable.
135             if (System.getenv("ANDROID_DISABLE_LAYOUT") == null) {      //$NON-NLS-1$
136                 if (mGraphicalEditor == null) {
137
138                     if (System.getenv("USE_GLE2") != null) {            //$NON-NLS-1$ //$NON-NLS-2$
139                         mGraphicalEditor = new GraphicalEditorPart(this);
140                     } else {
141                         mGraphicalEditor = new GraphicalLayoutEditor(this);
142                     }
143
144                     mGraphicalEditorIndex = addPage(mGraphicalEditor, getEditorInput());
145                     setPageText(mGraphicalEditorIndex, mGraphicalEditor.getTitle());
146                 } else {
147                     mGraphicalEditor.reloadEditor();
148                 }
149
150                 // update the config based on the opened file.
151                 IEditorInput input = getEditorInput();
152                 if (input instanceof FileEditorInput) {
153                     FileEditorInput fileInput = (FileEditorInput)input;
154                     ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(
155                             fileInput.getFile());
156                     if (resFolder != null) {
157                         mGraphicalEditor.editNewFile(resFolder.getConfiguration());
158                     }
159                 }
160
161                 // put in place the listener to handle layout recompute only when needed.
162                 getSite().getPage().addPartListener(this);
163             }
164         } catch (PartInitException e) {
165             AdtPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$
166         }
167      }
168
169     /* (non-java doc)
170      * Change the tab/title name to include the name of the layout.
171      */
172     @Override
173     protected void setInput(IEditorInput input) {
174         super.setInput(input);
175         handleNewInput(input);
176     }
177
178     /*
179      * (non-Javadoc)
180      * @see org.eclipse.ui.part.EditorPart#setInputWithNotify(org.eclipse.ui.IEditorInput)
181      */
182     @Override
183     protected void setInputWithNotify(IEditorInput input) {
184         super.setInputWithNotify(input);
185         handleNewInput(input);
186     }
187
188     /**
189      * Called to replace the current {@link IEditorInput} with another one.
190      * <p/>This is used when {@link MatchingStrategy} returned <code>true</code> which means we're
191      * opening a different configuration of the same layout.
192      */
193     public void showEditorInput(IEditorInput editorInput) {
194         // save the current editor input.
195         doSave(new NullProgressMonitor());
196
197         // get the current page
198         int currentPage = getActivePage();
199
200         // remove the pages, except for the graphical editor, which will be dynamically adapted
201         // to the new model.
202         // page after the graphical editor:
203         int count = getPageCount();
204         for (int i = count - 1 ; i > mGraphicalEditorIndex ; i--) {
205             removePage(i);
206         }
207         // pages before the graphical editor
208         for (int i = mGraphicalEditorIndex - 1 ; i >= 0 ; i--) {
209             removePage(i);
210         }
211
212         // set the current input.
213         setInputWithNotify(editorInput);
214
215         // re-create or reload the pages with the default page shown as the previous active page.
216         createAndroidPages();
217         selectDefaultPage(Integer.toString(currentPage));
218
219         // update the outline
220         if (mOutline != null && mGraphicalEditor != null) {
221             mOutline.reloadModel();
222         }
223     }
224
225     /**
226      * Processes the new XML Model, which XML root node is given.
227      *
228      * @param xml_doc The XML document, if available, or null if none exists.
229      */
230     @Override
231     protected void xmlModelChanged(Document xml_doc) {
232         // init the ui root on demand
233         initUiRootNode(false /*force*/);
234
235         mUiRootNode.loadFromXmlNode(xml_doc);
236
237         // update the model first, since it is used by the viewers.
238         super.xmlModelChanged(xml_doc);
239
240         if (mGraphicalEditor != null) {
241             mGraphicalEditor.onXmlModelChanged();
242         }
243
244         if (mOutline != null) {
245             mOutline.reloadModel();
246         }
247     }
248
249     /* (non-java doc)
250      * Returns the IContentOutlinePage when asked for it.
251      */
252     @SuppressWarnings("unchecked")
253     @Override
254     public Object getAdapter(Class adapter) {
255         // for the outline, force it to come from the Graphical Editor.
256         // This fixes the case where a layout file is opened in XML view first and the outline
257         // gets stuck in the XML outline.
258         if (IContentOutlinePage.class == adapter && mGraphicalEditor != null) {
259
260             if (mOutline == null && mGraphicalEditor instanceof GraphicalLayoutEditor) {
261                 // TODO add support for GLE2
262                 mOutline = new UiContentOutlinePage(
263                         (GraphicalLayoutEditor) mGraphicalEditor,
264                         new TreeViewer());
265             }
266
267             return mOutline;
268         }
269
270         if (IPropertySheetPage.class == adapter && mGraphicalEditor != null) {
271             if (mPropertyPage == null) {
272                 mPropertyPage = new UiPropertySheetPage();
273             }
274
275             return mPropertyPage;
276         }
277
278         // return default
279         return super.getAdapter(adapter);
280     }
281
282     @Override
283     protected void pageChange(int newPageIndex) {
284         super.pageChange(newPageIndex);
285
286         if (mGraphicalEditor != null) {
287             if (newPageIndex == mGraphicalEditorIndex) {
288                 mGraphicalEditor.activated();
289             } else {
290                 mGraphicalEditor.deactivated();
291             }
292         }
293     }
294
295     // ----- IPartListener Methods ----
296
297     public void partActivated(IWorkbenchPart part) {
298         if (part == this) {
299             if (mGraphicalEditor != null) {
300                 if (getActivePage() == mGraphicalEditorIndex) {
301                     mGraphicalEditor.activated();
302                 } else {
303                     mGraphicalEditor.deactivated();
304                 }
305             }
306         }
307     }
308
309     public void partBroughtToTop(IWorkbenchPart part) {
310         partActivated(part);
311     }
312
313     public void partClosed(IWorkbenchPart part) {
314         // pass
315     }
316
317     public void partDeactivated(IWorkbenchPart part) {
318         if (part == this) {
319             if (mGraphicalEditor != null && getActivePage() == mGraphicalEditorIndex) {
320                 mGraphicalEditor.deactivated();
321             }
322         }
323     }
324
325     public void partOpened(IWorkbenchPart part) {
326         EclipseUiHelper.showView(EclipseUiHelper.CONTENT_OUTLINE_VIEW_ID, false /* activate */);
327         EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID, false /* activate */);
328     }
329
330     public class UiEditorActions extends UiActions {
331
332         @Override
333         protected UiDocumentNode getRootNode() {
334             return mUiRootNode;
335         }
336
337         // Select the new item
338         @Override
339         protected void selectUiNode(UiElementNode uiNodeToSelect) {
340             mGraphicalEditor.selectModel(uiNodeToSelect);
341         }
342
343         @Override
344         public void commitPendingXmlChanges() {
345             // Pass. There is nothing to commit before the XML is changed here.
346         }
347     }
348
349     public UiEditorActions getUiEditorActions() {
350         if (mUiEditorActions == null) {
351             mUiEditorActions = new UiEditorActions();
352         }
353         return mUiEditorActions;
354     }
355
356     // ---- Local Methods ----
357
358     /**
359      * Returns true if the Graphics editor page is visible. This <b>must</b> be
360      * called from the UI thread.
361      */
362     public boolean isGraphicalEditorActive() {
363         IWorkbenchPartSite workbenchSite = getSite();
364         IWorkbenchPage workbenchPage = workbenchSite.getPage();
365
366         // check if the editor is visible in the workbench page
367         if (workbenchPage.isPartVisible(this) && workbenchPage.getActiveEditor() == this) {
368             // and then if the page of the editor is visible (not to be confused with
369             // the workbench page)
370             return mGraphicalEditorIndex == getActivePage();
371         }
372
373         return false;
374     }
375
376     @Override
377     protected void initUiRootNode(boolean force) {
378         // The root UI node is always created, even if there's no corresponding XML node.
379         if (mUiRootNode == null || force) {
380             // get the target data from the opened file (and its project)
381             AndroidTargetData data = getTargetData();
382
383             Document doc = null;
384             if (mUiRootNode != null) {
385                 doc = mUiRootNode.getXmlDocument();
386             }
387
388             DocumentDescriptor desc;
389             if (data == null) {
390                 desc = new DocumentDescriptor("temp", null /*children*/);
391             } else {
392                 desc = data.getLayoutDescriptors().getDescriptor();
393             }
394
395             // get the descriptors from the data.
396             mUiRootNode = (UiDocumentNode) desc.createUiNode();
397             mUiRootNode.setEditor(this);
398
399             onDescriptorsChanged(doc);
400         }
401     }
402
403     private void onDescriptorsChanged(Document document) {
404         if (document != null) {
405             mUiRootNode.loadFromXmlNode(document);
406         } else {
407             mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlDocument());
408         }
409
410         if (mOutline != null) {
411             mOutline.reloadModel();
412         }
413
414         if (mGraphicalEditor != null) {
415             mGraphicalEditor.reloadEditor();
416             mGraphicalEditor.reloadPalette();
417             mGraphicalEditor.reloadConfigurationUi(true /*notify listener */);
418             mGraphicalEditor.recomputeLayout();
419         }
420     }
421
422     /**
423      * Handles a new input, and update the part name.
424      * @param input the new input.
425      */
426     private void handleNewInput(IEditorInput input) {
427         if (input instanceof FileEditorInput) {
428             FileEditorInput fileInput = (FileEditorInput) input;
429             IFile file = fileInput.getFile();
430             setPartName(String.format("%1$s",
431                     file.getName()));
432         }
433     }
434 }