Merge commit 'remotes/korg/cupcake'
Conflicts: build/sdk.atree ide/eclipse/.classpath tools/activitycreator/src/com/android/activitycreator/ActivityCreator.java tools/eclipse/scripts/create_adt_symlinks.sh tools/eclipse/scripts/create_all_symlinks.sh tools/eclipse/scripts/create_bridge_symlinks.sh tools/eclipse/scripts/create_common_symlinks.sh tools/eclipse/scripts/create_ddms_symlinks.sh tools/eclipse/scripts/create_editors_symlinks.sh tools/eclipse/scripts/create_test_symlinks.sh tools/scripts/build.template
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
|
||||
<accessrules>
|
||||
<accessrule kind="accessible" pattern="org/eclipse/wst/**/internal/**"/>
|
||||
</accessrules>
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="kxml2-2.3.0.jar"/>
|
||||
<classpathentry kind="lib" path="layoutlib_api.jar"/>
|
||||
<classpathentry kind="lib" path="ninepatch.jar"/>
|
||||
<classpathentry kind="lib" path="layoutlib_utils.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>editors</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -1,56 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Android Editors
|
||||
Bundle-SymbolicName: com.android.ide.eclipse.editors;singleton:=true
|
||||
Bundle-Version: 0.8.1.qualifier
|
||||
Bundle-Activator: com.android.ide.eclipse.editors.EditorsPlugin
|
||||
Bundle-Vendor: The Android Open Source Project
|
||||
Require-Bundle: com.android.ide.eclipse.common,
|
||||
org.eclipse.ui,
|
||||
org.eclipse.core.runtime,
|
||||
org.eclipse.core.resources,
|
||||
org.eclipse.ui.editors,
|
||||
org.eclipse.jface.text,
|
||||
org.eclipse.ui.ide,
|
||||
org.eclipse.wst.sse.ui,
|
||||
org.eclipse.wst.xml.ui,
|
||||
org.eclipse.wst.xml.core,
|
||||
org.eclipse.wst.sse.core,
|
||||
org.eclipse.ui.forms,
|
||||
org.eclipse.jdt.core,
|
||||
org.eclipse.ui.browser,
|
||||
org.eclipse.jdt.ui,
|
||||
org.eclipse.gef,
|
||||
org.eclipse.ui.views,
|
||||
org.eclipse.ui.console
|
||||
Eclipse-LazyStart: true
|
||||
Export-Package: com.android.ide.eclipse.editors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.descriptors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.layout;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.layout.descriptors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.layout.parts;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.layout.uimodel;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.manifest;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.manifest.descriptors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.manifest.model;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.manifest.pages;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.menu;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.menu.descriptors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.configurations;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.descriptors;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.explorer;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.manager;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.manager.files;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.resources.uimodel;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.ui;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.ui.tree;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.uimodel;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.wizards;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.xml;x-friends:="com.android.ide.eclipse.tests",
|
||||
com.android.ide.eclipse.editors.xml.descriptors;x-friends:="com.android.ide.eclipse.tests"
|
||||
Bundle-ClassPath: kxml2-2.3.0.jar,
|
||||
.,
|
||||
layoutlib_api.jar,
|
||||
ninepatch.jar,
|
||||
layoutlib_utils.jar
|
||||
@@ -1,224 +0,0 @@
|
||||
*Eclipse Public License - v 1.0*
|
||||
|
||||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
|
||||
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
|
||||
THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
|
||||
*1. DEFINITIONS*
|
||||
|
||||
"Contribution" means:
|
||||
|
||||
a) in the case of the initial Contributor, the initial code and
|
||||
documentation distributed under this Agreement, and
|
||||
b) in the case of each subsequent Contributor:
|
||||
|
||||
i) changes to the Program, and
|
||||
|
||||
ii) additions to the Program;
|
||||
|
||||
where such changes and/or additions to the Program originate from and
|
||||
are distributed by that particular Contributor. A Contribution
|
||||
'originates' from a Contributor if it was added to the Program by such
|
||||
Contributor itself or anyone acting on such Contributor's behalf.
|
||||
Contributions do not include additions to the Program which: (i) are
|
||||
separate modules of software distributed in conjunction with the Program
|
||||
under their own license agreement, and (ii) are not derivative works of
|
||||
the Program.
|
||||
|
||||
"Contributor" means any person or entity that distributes the Program.
|
||||
|
||||
"Licensed Patents " mean patent claims licensable by a Contributor which
|
||||
are necessarily infringed by the use or sale of its Contribution alone
|
||||
or when combined with the Program.
|
||||
|
||||
"Program" means the Contributions distributed in accordance with this
|
||||
Agreement.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement,
|
||||
including all Contributors.
|
||||
|
||||
*2. GRANT OF RIGHTS*
|
||||
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby
|
||||
grants Recipient a non-exclusive, worldwide, royalty-free copyright
|
||||
license to reproduce, prepare derivative works of, publicly display,
|
||||
publicly perform, distribute and sublicense the Contribution of such
|
||||
Contributor, if any, and such derivative works, in source code and
|
||||
object code form.
|
||||
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby
|
||||
grants Recipient a non-exclusive, worldwide, royalty-free patent license
|
||||
under Licensed Patents to make, use, sell, offer to sell, import and
|
||||
otherwise transfer the Contribution of such Contributor, if any, in
|
||||
source code and object code form. This patent license shall apply to the
|
||||
combination of the Contribution and the Program if, at the time the
|
||||
Contribution is added by the Contributor, such addition of the
|
||||
Contribution causes such combination to be covered by the Licensed
|
||||
Patents. The patent license shall not apply to any other combinations
|
||||
which include the Contribution. No hardware per se is licensed hereunder.
|
||||
|
||||
c) Recipient understands that although each Contributor grants the
|
||||
licenses to its Contributions set forth herein, no assurances are
|
||||
provided by any Contributor that the Program does not infringe the
|
||||
patent or other intellectual property rights of any other entity. Each
|
||||
Contributor disclaims any liability to Recipient for claims brought by
|
||||
any other entity based on infringement of intellectual property rights
|
||||
or otherwise. As a condition to exercising the rights and licenses
|
||||
granted hereunder, each Recipient hereby assumes sole responsibility to
|
||||
secure any other intellectual property rights needed, if any. For
|
||||
example, if a third party patent license is required to allow Recipient
|
||||
to distribute the Program, it is Recipient's responsibility to acquire
|
||||
that license before distributing the Program.
|
||||
|
||||
d) Each Contributor represents that to its knowledge it has sufficient
|
||||
copyright rights in its Contribution, if any, to grant the copyright
|
||||
license set forth in this Agreement.
|
||||
|
||||
*3. REQUIREMENTS*
|
||||
|
||||
A Contributor may choose to distribute the Program in object code form
|
||||
under its own license agreement, provided that:
|
||||
|
||||
a) it complies with the terms and conditions of this Agreement; and
|
||||
|
||||
b) its license agreement:
|
||||
|
||||
i) effectively disclaims on behalf of all Contributors all warranties
|
||||
and conditions, express and implied, including warranties or conditions
|
||||
of title and non-infringement, and implied warranties or conditions of
|
||||
merchantability and fitness for a particular purpose;
|
||||
|
||||
ii) effectively excludes on behalf of all Contributors all liability for
|
||||
damages, including direct, indirect, special, incidental and
|
||||
consequential damages, such as lost profits;
|
||||
|
||||
iii) states that any provisions which differ from this Agreement are
|
||||
offered by that Contributor alone and not by any other party; and
|
||||
|
||||
iv) states that source code for the Program is available from such
|
||||
Contributor, and informs licensees how to obtain it in a reasonable
|
||||
manner on or through a medium customarily used for software exchange.
|
||||
|
||||
When the Program is made available in source code form:
|
||||
|
||||
a) it must be made available under this Agreement; and
|
||||
|
||||
b) a copy of this Agreement must be included with each copy of the Program.
|
||||
|
||||
Contributors may not remove or alter any copyright notices contained
|
||||
within the Program.
|
||||
|
||||
Each Contributor must identify itself as the originator of its
|
||||
Contribution, if any, in a manner that reasonably allows subsequent
|
||||
Recipients to identify the originator of the Contribution.
|
||||
|
||||
*4. COMMERCIAL DISTRIBUTION*
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities
|
||||
with respect to end users, business partners and the like. While this
|
||||
license is intended to facilitate the commercial use of the Program, the
|
||||
Contributor who includes the Program in a commercial product offering
|
||||
should do so in a manner which does not create potential liability for
|
||||
other Contributors. Therefore, if a Contributor includes the Program in
|
||||
a commercial product offering, such Contributor ("Commercial
|
||||
Contributor") hereby agrees to defend and indemnify every other
|
||||
Contributor ("Indemnified Contributor") against any losses, damages and
|
||||
costs (collectively "Losses") arising from claims, lawsuits and other
|
||||
legal actions brought by a third party against the Indemnified
|
||||
Contributor to the extent caused by the acts or omissions of such
|
||||
Commercial Contributor in connection with its distribution of the
|
||||
Program in a commercial product offering. The obligations in this
|
||||
section do not apply to any claims or Losses relating to any actual or
|
||||
alleged intellectual property infringement. In order to qualify, an
|
||||
Indemnified Contributor must: a) promptly notify the Commercial
|
||||
Contributor in writing of such claim, and b) allow the Commercial
|
||||
Contributor to control, and cooperate with the Commercial Contributor
|
||||
in, the defense and any related settlement negotiations. The Indemnified
|
||||
Contributor may participate in any such claim at its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial
|
||||
product offering, Product X. That Contributor is then a Commercial
|
||||
Contributor. If that Commercial Contributor then makes performance
|
||||
claims, or offers warranties related to Product X, those performance
|
||||
claims and warranties are such Commercial Contributor's responsibility
|
||||
alone. Under this section, the Commercial Contributor would have to
|
||||
defend claims against the other Contributors related to those
|
||||
performance claims and warranties, and if a court requires any other
|
||||
Contributor to pay any damages as a result, the Commercial Contributor
|
||||
must pay those damages.
|
||||
|
||||
*5. NO WARRANTY*
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED
|
||||
ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES
|
||||
OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR
|
||||
A PARTICULAR PURPOSE. Each Recipient is solely responsible for
|
||||
determining the appropriateness of using and distributing the Program
|
||||
and assumes all risks associated with its exercise of rights under this
|
||||
Agreement , including but not limited to the risks and costs of program
|
||||
errors, compliance with applicable laws, damage to or loss of data,
|
||||
programs or equipment, and unavailability or interruption of operations.
|
||||
|
||||
*6. DISCLAIMER OF LIABILITY*
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
|
||||
ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
|
||||
WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
|
||||
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
|
||||
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
*7. GENERAL*
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under
|
||||
applicable law, it shall not affect the validity or enforceability of
|
||||
the remainder of the terms of this Agreement, and without further action
|
||||
by the parties hereto, such provision shall be reformed to the minimum
|
||||
extent necessary to make such provision valid and enforceable.
|
||||
|
||||
If Recipient institutes patent litigation against any entity (including
|
||||
a cross-claim or counterclaim in a lawsuit) alleging that the Program
|
||||
itself (excluding combinations of the Program with other software or
|
||||
hardware) infringes such Recipient's patent(s), then such Recipient's
|
||||
rights granted under Section 2(b) shall terminate as of the date such
|
||||
litigation is filed.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it fails
|
||||
to comply with any of the material terms or conditions of this Agreement
|
||||
and does not cure such failure in a reasonable period of time after
|
||||
becoming aware of such noncompliance. If all Recipient's rights under
|
||||
this Agreement terminate, Recipient agrees to cease use and distribution
|
||||
of the Program as soon as reasonably practicable. However, Recipient's
|
||||
obligations under this Agreement and any licenses granted by Recipient
|
||||
relating to the Program shall continue and survive.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement,
|
||||
but in order to avoid inconsistency the Agreement is copyrighted and may
|
||||
only be modified in the following manner. The Agreement Steward reserves
|
||||
the right to publish new versions (including revisions) of this
|
||||
Agreement from time to time. No one other than the Agreement Steward has
|
||||
the right to modify this Agreement. The Eclipse Foundation is the
|
||||
initial Agreement Steward. The Eclipse Foundation may assign the
|
||||
responsibility to serve as the Agreement Steward to a suitable separate
|
||||
entity. Each new version of the Agreement will be given a distinguishing
|
||||
version number. The Program (including Contributions) may always be
|
||||
distributed subject to the version of the Agreement under which it was
|
||||
received. In addition, after a new version of the Agreement is
|
||||
published, Contributor may elect to distribute the Program (including
|
||||
its Contributions) under the new version. Except as expressly stated in
|
||||
Sections 2(a) and 2(b) above, Recipient receives no rights or licenses
|
||||
to the intellectual property of any Contributor under this Agreement,
|
||||
whether expressly, by implication, estoppel or otherwise. All rights in
|
||||
the Program not expressly granted under this Agreement are reserved.
|
||||
|
||||
This Agreement is governed by the laws of the State of New York and the
|
||||
intellectual property laws of the United States of America. No party to
|
||||
this Agreement will bring a legal action under this Agreement more than
|
||||
one year after the cause of action arose. Each party waives its rights
|
||||
to a jury trial in any resulting litigation.
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.,\
|
||||
plugin.xml,\
|
||||
icons/,\
|
||||
layoutlib_api.jar,\
|
||||
kxml2-2.3.0.jar,\
|
||||
ninepatch.jar,\
|
||||
layoutlib_utils.jar
|
||||
|
Before Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 363 B |
|
Before Width: | Height: | Size: 107 B |
|
Before Width: | Height: | Size: 320 B |
|
Before Width: | Height: | Size: 157 B |
|
Before Width: | Height: | Size: 302 B |
|
Before Width: | Height: | Size: 194 B |
|
Before Width: | Height: | Size: 307 B |
|
Before Width: | Height: | Size: 287 B |
|
Before Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 265 B |
|
Before Width: | Height: | Size: 308 B |
|
Before Width: | Height: | Size: 325 B |
|
Before Width: | Height: | Size: 321 B |
|
Before Width: | Height: | Size: 321 B |
|
Before Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 137 B |
|
Before Width: | Height: | Size: 147 B |
|
Before Width: | Height: | Size: 562 B |
@@ -1,107 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?eclipse version="3.2"?>
|
||||
<plugin>
|
||||
<extension
|
||||
point="org.eclipse.ui.editors">
|
||||
<editor
|
||||
class="com.android.ide.eclipse.editors.manifest.ManifestEditor"
|
||||
default="true"
|
||||
filenames="AndroidManifest.xml"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.manifest.ManifestEditor"
|
||||
name="Android Manifest Editor">
|
||||
</editor>
|
||||
<editor
|
||||
class="com.android.ide.eclipse.editors.resources.ResourcesEditor"
|
||||
default="false"
|
||||
extensions="xml"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.resources.ResourcesEditor"
|
||||
name="Android Resource Editor">
|
||||
</editor>
|
||||
<editor
|
||||
class="com.android.ide.eclipse.editors.layout.LayoutEditor"
|
||||
default="false"
|
||||
extensions="xml"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.layout.LayoutEditor"
|
||||
matchingStrategy="com.android.ide.eclipse.editors.layout.MatchingStrategy"
|
||||
name="Android Layout Editor">
|
||||
</editor>
|
||||
<editor
|
||||
class="com.android.ide.eclipse.editors.menu.MenuEditor"
|
||||
default="false"
|
||||
extensions="xml"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.menu.MenuEditor"
|
||||
name="Android Menu Editor">
|
||||
</editor>
|
||||
<editor
|
||||
class="com.android.ide.eclipse.editors.xml.XmlEditor"
|
||||
default="false"
|
||||
extensions="xml"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.xml.XmlEditor"
|
||||
name="Android Xml Resources Editor">
|
||||
</editor>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.wst.sse.ui.editorConfiguration">
|
||||
<sourceViewerConfiguration
|
||||
class="com.android.ide.eclipse.editors.manifest.ManifestSourceViewerConfig"
|
||||
target="com.android.ide.eclipse.editors.manifest.ManifestEditor">
|
||||
</sourceViewerConfiguration>
|
||||
<sourceViewerConfiguration
|
||||
class="com.android.ide.eclipse.editors.resources.ResourcesSourceViewerConfig"
|
||||
target="com.android.ide.eclipse.editors.resources.ResourcesEditor">
|
||||
</sourceViewerConfiguration>
|
||||
<sourceViewerConfiguration
|
||||
class="com.android.ide.eclipse.editors.layout.LayoutSourceViewerConfig"
|
||||
target="com.android.ide.eclipse.editors.layout.LayoutEditor">
|
||||
</sourceViewerConfiguration>
|
||||
<sourceViewerConfiguration
|
||||
class="com.android.ide.eclipse.editors.menu.MenuSourceViewerConfig"
|
||||
target="com.android.ide.eclipse.editors.menu.MenuEditor">
|
||||
</sourceViewerConfiguration>
|
||||
<sourceViewerConfiguration
|
||||
class="com.android.ide.eclipse.editors.xml.XmlSourceViewerConfig"
|
||||
target="com.android.ide.eclipse.editors.xml.XmlEditor">
|
||||
</sourceViewerConfiguration>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.views">
|
||||
<view
|
||||
allowMultiple="false"
|
||||
category="com.android.ide.eclipse.ddms.views.category"
|
||||
class="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.resources.explorer.ResourceExplorerView"
|
||||
name="Resource Explorer">
|
||||
</view>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.newWizards">
|
||||
<wizard
|
||||
canFinishEarly="false"
|
||||
category="com.android.ide.eclipse.wizards.category"
|
||||
class="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard"
|
||||
finalPerspective="org.eclipse.jdt.ui.JavaPerspective"
|
||||
hasPages="true"
|
||||
icon="icons/android.png"
|
||||
id="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard"
|
||||
name="Android XML File"
|
||||
preferredPerspectives="org.eclipse.jdt.ui.JavaPerspective"
|
||||
project="false">
|
||||
</wizard>
|
||||
</extension>
|
||||
<extension
|
||||
point="org.eclipse.ui.perspectiveExtensions">
|
||||
<perspectiveExtension
|
||||
targetID="org.eclipse.jdt.ui.JavaPerspective">
|
||||
<newWizardShortcut
|
||||
id="com.android.ide.eclipse.editors.wizards.NewXmlFileWizard">
|
||||
</newWizardShortcut>
|
||||
</perspectiveExtension>
|
||||
</extension>
|
||||
|
||||
</plugin>
|
||||
@@ -1,733 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.resources.FrameworkResourceManager;
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.TextValueDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiFlagAttributeNode;
|
||||
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.ITextViewer;
|
||||
import org.eclipse.jface.text.TextSelection;
|
||||
import org.eclipse.jface.text.contentassist.CompletionProposal;
|
||||
import org.eclipse.jface.text.contentassist.ICompletionProposal;
|
||||
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
|
||||
import org.eclipse.jface.text.contentassist.IContextInformation;
|
||||
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
|
||||
import org.eclipse.jface.text.source.ISourceViewer;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.ui.IEditorPart;
|
||||
import org.eclipse.ui.IWorkbenchPage;
|
||||
import org.eclipse.ui.IWorkbenchWindow;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
import org.eclipse.wst.sse.core.StructuredModelManager;
|
||||
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
|
||||
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Content Assist Processor for Android XML files
|
||||
*/
|
||||
public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
||||
|
||||
/** Regexp to detect a full attribute after an element tag.
|
||||
* <pre>Syntax:
|
||||
* name = "..." quoted string with all but < and "
|
||||
* or:
|
||||
* name = '...' quoted string with all but < and '
|
||||
* </pre>
|
||||
*/
|
||||
private static Pattern sFirstAttribute = Pattern.compile(
|
||||
"^ *[a-zA-Z_:]+ *= *(?:\"[^<\"]*\"|'[^<']*')"); //$NON-NLS-1$
|
||||
|
||||
/** Regexp to detect an element tag name */
|
||||
private static Pattern sFirstElementWord = Pattern.compile("^[a-zA-Z0-9_:]+"); //$NON-NLS-1$
|
||||
|
||||
/** Regexp to detect whitespace */
|
||||
private static Pattern sWhitespace = Pattern.compile("\\s+"); //$NON-NLS-1$
|
||||
|
||||
protected final static String ROOT_ELEMENT = "";
|
||||
|
||||
/** Descriptor of the root of the XML hierarchy. This a "fake" ElementDescriptor which
|
||||
* is used to list all the possible roots given by actual implementations. */
|
||||
private ElementDescriptor mRootDescriptor;
|
||||
|
||||
/**
|
||||
* Constructor for AndroidContentAssist
|
||||
* @param rootElementDescriptors The valid root elements of the XML hierarchy
|
||||
*/
|
||||
public AndroidContentAssist(ElementDescriptor[] rootElementDescriptors) {
|
||||
mRootDescriptor = new ElementDescriptor("", rootElementDescriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of completion proposals based on the
|
||||
* specified location within the document that corresponds
|
||||
* to the current cursor position within the text viewer.
|
||||
*
|
||||
* @param viewer the viewer whose document is used to compute the proposals
|
||||
* @param offset an offset within the document for which completions should be computed
|
||||
* @return an array of completion proposals or <code>null</code> if no proposals are possible
|
||||
*
|
||||
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
|
||||
*/
|
||||
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
|
||||
|
||||
AndroidEditor editor = getAndroidEditor(viewer);
|
||||
UiElementNode rootUiNode = editor.getUiRootNode();
|
||||
|
||||
Object[] choices = null; /* An array of ElementDescriptor, or AttributeDescriptor
|
||||
or String or null */
|
||||
String parent = ""; //$NON-NLS-1$
|
||||
String wordPrefix = extractElementPrefix(viewer, offset);
|
||||
char needTag = 0;
|
||||
boolean isElement = false;
|
||||
boolean isAttribute = false;
|
||||
|
||||
Node currentNode = getNode(viewer, offset);
|
||||
if (currentNode == null)
|
||||
return null;
|
||||
|
||||
// check to see if we can find a UiElementNode matching this XML node
|
||||
UiElementNode currentUiNode =
|
||||
rootUiNode == null ? null : rootUiNode.findXmlNode(currentNode);
|
||||
|
||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||
parent = currentNode.getNodeName();
|
||||
|
||||
if (wordPrefix.equals(parent)) {
|
||||
// We are still editing the element's tag name, not the attributes
|
||||
// (the element's tag name may not even be complete)
|
||||
isElement = true;
|
||||
choices = getChoicesForElement(parent, currentNode);
|
||||
} else {
|
||||
// We're not editing the current node name, so we might be editing its
|
||||
// attributes instead...
|
||||
isAttribute = true;
|
||||
AttribInfo info = parseAttributeInfo(viewer, offset);
|
||||
if (info != null) {
|
||||
// We're editing attributes in an element node (either the attributes' names
|
||||
// or their values).
|
||||
choices = getChoicesForAttribute(parent, currentNode, currentUiNode, info);
|
||||
|
||||
if (info.correctedPrefix != null) {
|
||||
wordPrefix = info.correctedPrefix;
|
||||
}
|
||||
needTag = info.needTag;
|
||||
}
|
||||
}
|
||||
} else if (currentNode.getNodeType() == Node.TEXT_NODE) {
|
||||
isElement = true;
|
||||
// Examine the parent of the text node.
|
||||
choices = getChoicesForTextNode(currentNode);
|
||||
}
|
||||
|
||||
// Abort if we can't recognize the context or there are no completion choices
|
||||
if (choices == null || choices.length == 0) return null;
|
||||
|
||||
if (isElement) {
|
||||
// If we found some suggestions, do we need to add an opening "<" bracket
|
||||
// for the element? We don't if the cursor is right after "<" or "</".
|
||||
// Per XML Spec, there's no whitespace between "<" or "</" and the tag name.
|
||||
int offset2 = offset - wordPrefix.length() - 1;
|
||||
int c1 = extractChar(viewer, offset2);
|
||||
if (!((c1 == '<') || (c1 == '/' && extractChar(viewer, offset2 - 1) == '<'))) {
|
||||
needTag = '<';
|
||||
}
|
||||
}
|
||||
|
||||
// get the selection length
|
||||
int selectionLength = 0;
|
||||
ISelection selection = viewer.getSelectionProvider().getSelection();
|
||||
if (selection instanceof TextSelection) {
|
||||
TextSelection textSelection = (TextSelection)selection;
|
||||
selectionLength = textSelection.getLength();
|
||||
}
|
||||
|
||||
return computeProposals(offset, currentNode, choices, wordPrefix, needTag,
|
||||
isAttribute, selectionLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace prefix matching the Android Resource URI.
|
||||
* If no such declaration is found, returns the default "android" prefix.
|
||||
*
|
||||
* @param node The current node. Must not be null.
|
||||
* @param nsUri The namespace URI of which the prefix is to be found,
|
||||
* e.g. AndroidConstants.NS_RESOURCES
|
||||
* @return The first prefix declared or the default "android" prefix.
|
||||
*/
|
||||
private String lookupNamespacePrefix(Node node, String nsUri) {
|
||||
// Note: Node.lookupPrefix is not implemented in wst/xml/core NodeImpl.java
|
||||
// The following emulates this:
|
||||
// String prefix = node.lookupPrefix(AndroidConstants.NS_RESOURCES);
|
||||
|
||||
if (XmlnsAttributeDescriptor.XMLNS_URI.equals(nsUri)) {
|
||||
return "xmlns"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
HashSet<String> visited = new HashSet<String>();
|
||||
|
||||
String prefix = null;
|
||||
for (; prefix == null &&
|
||||
node != null &&
|
||||
node.getNodeType() == Node.ELEMENT_NODE;
|
||||
node = node.getParentNode()) {
|
||||
NamedNodeMap attrs = node.getAttributes();
|
||||
for (int n = attrs.getLength() - 1; n >= 0; --n) {
|
||||
Node attr = attrs.item(n);
|
||||
if ("xmlns".equals(attr.getPrefix())) { //$NON-NLS-1$
|
||||
String uri = attr.getNodeValue();
|
||||
if (AndroidConstants.NS_RESOURCES.equals(uri)) {
|
||||
return attr.getLocalName();
|
||||
}
|
||||
visited.add(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use a sensible default prefix if we can't find one.
|
||||
// We need to make sure the prefix is not one that was declared in the scope
|
||||
// visited above.
|
||||
prefix = AndroidConstants.NS_RESOURCES.equals(nsUri) ? "android" : "ns"; //$NON-NLS-1$ //$NON-NLS-2$
|
||||
String base = prefix;
|
||||
for (int i = 1; visited.contains(prefix); i++) {
|
||||
prefix = base + Integer.toString(i);
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the choices when the user is editing the name of an XML element.
|
||||
* <p/>
|
||||
* The user is editing the name of an element (the "parent").
|
||||
* Find the grand-parent and if one is found, return its children element list.
|
||||
* The name which is being edited should be one of those.
|
||||
* <p/>
|
||||
* Example: <manifest><applic*cursor* => returns the list of all elements that
|
||||
* can be found under <manifest>, of which <application> is one of the choices.
|
||||
*
|
||||
* @return an ElementDescriptor[] or null if no valid element was found.
|
||||
*/
|
||||
private Object[] getChoicesForElement(String parent, Node current_node) {
|
||||
ElementDescriptor grandparent = null;
|
||||
if (current_node.getParentNode().getNodeType() == Node.ELEMENT_NODE) {
|
||||
grandparent = getDescriptor(current_node.getParentNode().getNodeName());
|
||||
} else if (current_node.getParentNode().getNodeType() == Node.DOCUMENT_NODE) {
|
||||
grandparent = mRootDescriptor;
|
||||
}
|
||||
if (grandparent != null) {
|
||||
for (ElementDescriptor e : grandparent.getChildren()) {
|
||||
if (e.getXmlName().startsWith(parent)) {
|
||||
return grandparent.getChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the choices when the user is editing an XML attribute.
|
||||
* <p/>
|
||||
* In input, attrInfo contains details on the analyzed context, namely whether the
|
||||
* user is editing an attribute value (isInValue) or an attribute name.
|
||||
* <p/>
|
||||
* In output, attrInfo also contains two possible new values (this is a hack to circumvent
|
||||
* the lack of out-parameters in Java):
|
||||
* - AttribInfo.correctedPrefix if the user has been editing an attribute value and it has
|
||||
* been detected that what the user typed is different from what extractElementPrefix()
|
||||
* predicted. This happens because extractElementPrefix() stops when a character that
|
||||
* cannot be an element name appears whereas parseAttributeInfo() uses a grammar more
|
||||
* lenient as suitable for attribute values.
|
||||
* - AttribInfo.needTag will be non-zero if we find that the attribute completion proposal
|
||||
* must be double-quoted.
|
||||
* @param currentUiNode
|
||||
*
|
||||
* @return an AttributeDescriptor[] if the user is editing an attribute name.
|
||||
* a String[] if the user is editing an attribute value with some known values,
|
||||
* or null if nothing is known about the context.
|
||||
*/
|
||||
private Object[] getChoicesForAttribute(String parent,
|
||||
Node currentNode, UiElementNode currentUiNode, AttribInfo attrInfo) {
|
||||
Object[] choices = null;
|
||||
if (attrInfo.isInValue) {
|
||||
// Editing an attribute's value... Get the attribute name and then the
|
||||
// possible choice for the tuple(parent,attribute)
|
||||
String value = attrInfo.value;
|
||||
if (value.startsWith("'") || value.startsWith("\"")) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||
value = value.substring(1);
|
||||
// The prefix that was found at the beginning only scan for characters
|
||||
// valid of tag name. We now know the real prefix for this attribute's
|
||||
// value, which is needed to generate the completion choices below.
|
||||
attrInfo.correctedPrefix = value;
|
||||
} else {
|
||||
attrInfo.needTag = '"';
|
||||
}
|
||||
|
||||
if (currentUiNode != null) {
|
||||
// look for an UI attribute matching the current attribute name
|
||||
String attrName = attrInfo.name;
|
||||
|
||||
UiAttributeNode currAttrNode = null;
|
||||
for (UiAttributeNode attrNode : currentUiNode.getUiAttributes()) {
|
||||
if (attrNode.getDescriptor().getXmlLocalName().equals(attrName)) {
|
||||
currAttrNode = attrNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currAttrNode != null) {
|
||||
choices = currAttrNode.getPossibleValues();
|
||||
|
||||
if (currAttrNode instanceof UiFlagAttributeNode) {
|
||||
// A "flag" can consist of several values separated by "or" (|).
|
||||
// If the correct prefix contains such a pipe character, we change
|
||||
// it so that only the currently edited value is completed.
|
||||
int pos = value.indexOf('|');
|
||||
if (pos >= 0) {
|
||||
attrInfo.correctedPrefix = value = value.substring(pos + 1);
|
||||
attrInfo.needTag = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (choices == null) {
|
||||
// fallback on the older descriptor-only based lookup.
|
||||
|
||||
// in order to properly handle the special case of the name attribute in
|
||||
// the action tag, we need the grandparent of the action node, to know
|
||||
// what type of actions we need.
|
||||
// e.g. activity -> intent-filter -> action[@name]
|
||||
String greatGrandParentName = null;
|
||||
Node grandParent = currentNode.getParentNode();
|
||||
if (grandParent != null) {
|
||||
Node greatGrandParent = grandParent.getParentNode();
|
||||
if (greatGrandParent != null) {
|
||||
greatGrandParentName = greatGrandParent.getLocalName();
|
||||
}
|
||||
}
|
||||
choices = FrameworkResourceManager.getInstance().getValues(
|
||||
parent, attrInfo.name, greatGrandParentName);
|
||||
}
|
||||
} else {
|
||||
// Editing an attribute's name... Get attributes valid for the parent node.
|
||||
if (currentUiNode != null) {
|
||||
choices = currentUiNode.getAttributeDescriptors();
|
||||
} else {
|
||||
ElementDescriptor parent_desc = getDescriptor(parent);
|
||||
choices = parent_desc.getAttributes();
|
||||
}
|
||||
}
|
||||
return choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the choices when the user is editing an XML text node.
|
||||
* <p/>
|
||||
* This means the user is editing outside of any XML element or attribute.
|
||||
* Simply return the list of XML elements that can be present there, based on the
|
||||
* parent of the current node.
|
||||
*
|
||||
* @return An ElementDescriptor[] or null.
|
||||
*/
|
||||
private Object[] getChoicesForTextNode(Node currentNode) {
|
||||
Object[] choices = null;
|
||||
String parent;
|
||||
Node parent_node = currentNode.getParentNode();
|
||||
if (parent_node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
// We're editing a text node which parent is an element node. Limit
|
||||
// content assist to elements valid for the parent.
|
||||
parent = parent_node.getNodeName();
|
||||
ElementDescriptor desc = getDescriptor(parent);
|
||||
if (desc != null) {
|
||||
choices = desc.getChildren();
|
||||
}
|
||||
} else if (parent_node.getNodeType() == Node.DOCUMENT_NODE) {
|
||||
// We're editing a text node at the first level (i.e. root node).
|
||||
// Limit content assist to the only valid root elements.
|
||||
choices = mRootDescriptor.getChildren();
|
||||
}
|
||||
return choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of choices found, generates the proposals to be displayed to the user.
|
||||
* <p/>
|
||||
* Choices is an object array. Items of the array can be:
|
||||
* - ElementDescriptor: a possible element descriptor which XML name should be completed.
|
||||
* - AttributeDescriptor: a possible attribute descriptor which XML name should be completed.
|
||||
* - String: string values to display as-is to the user. Typically those are possible
|
||||
* values for a given attribute.
|
||||
*
|
||||
* @return The ICompletionProposal[] to display to the user.
|
||||
*/
|
||||
private ICompletionProposal[] computeProposals(int offset, Node currentNode,
|
||||
Object[] choices, String wordPrefix, char need_tag,
|
||||
boolean is_attribute, int selectionLength) {
|
||||
ArrayList<CompletionProposal> proposals = new ArrayList<CompletionProposal>();
|
||||
HashMap<String, String> nsUriMap = new HashMap<String, String>();
|
||||
|
||||
for (Object choice : choices) {
|
||||
String keyword = null;
|
||||
String nsPrefix = null;
|
||||
Image icon = null;
|
||||
String tooltip = null;
|
||||
if (choice instanceof ElementDescriptor) {
|
||||
keyword = ((ElementDescriptor)choice).getXmlName();
|
||||
icon = ((ElementDescriptor)choice).getIcon();
|
||||
tooltip = DescriptorsUtils.formatTooltip(((ElementDescriptor)choice).getTooltip());
|
||||
} else if (choice instanceof TextValueDescriptor) {
|
||||
continue; // Value nodes are not part of the completion choices
|
||||
} else if (choice instanceof SeparatorAttributeDescriptor) {
|
||||
continue; // not real attribute descriptors
|
||||
} else if (choice instanceof AttributeDescriptor) {
|
||||
keyword = ((AttributeDescriptor)choice).getXmlLocalName();
|
||||
icon = ((AttributeDescriptor)choice).getIcon();
|
||||
if (choice instanceof TextAttributeDescriptor) {
|
||||
tooltip = ((TextAttributeDescriptor) choice).getTooltip();
|
||||
}
|
||||
|
||||
String nsUri = ((AttributeDescriptor)choice).getNamespaceUri();
|
||||
nsPrefix = nsUriMap.get(nsUri);
|
||||
if (nsPrefix == null) {
|
||||
nsPrefix = lookupNamespacePrefix(currentNode, nsUri);
|
||||
nsUriMap.put(nsUri, nsPrefix);
|
||||
}
|
||||
if (nsPrefix != null) {
|
||||
nsPrefix += ":"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
} else if (choice instanceof String) {
|
||||
keyword = (String) choice;
|
||||
} else {
|
||||
continue; // discard unknown choice
|
||||
}
|
||||
|
||||
if (keyword.startsWith(wordPrefix) ||
|
||||
(nsPrefix != null && keyword.startsWith(nsPrefix))) {
|
||||
if (nsPrefix != null) {
|
||||
keyword = nsPrefix + keyword;
|
||||
}
|
||||
String end_tag = ""; //$NON-NLS-1$
|
||||
if (need_tag != 0) {
|
||||
if (need_tag == '"') {
|
||||
keyword = need_tag + keyword;
|
||||
end_tag = String.valueOf(need_tag);
|
||||
} else if (need_tag == '<') {
|
||||
if (elementCanHaveChildren(choice)) {
|
||||
end_tag = String.format("></%1$s>", keyword); //$NON-NLS-1$
|
||||
keyword = need_tag + keyword;
|
||||
} else {
|
||||
keyword = need_tag + keyword;
|
||||
end_tag = "/>"; //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
}
|
||||
CompletionProposal proposal = new CompletionProposal(
|
||||
keyword + end_tag, // String replacementString
|
||||
offset - wordPrefix.length(), // int replacementOffset
|
||||
wordPrefix.length() + selectionLength, // int replacementLength
|
||||
keyword.length(), // int cursorPosition (rel. to rplcmntOffset)
|
||||
icon, // Image image
|
||||
null, // String displayString
|
||||
null, // IContextInformation contextInformation
|
||||
tooltip // String additionalProposalInfo
|
||||
);
|
||||
|
||||
proposals.add(proposal);
|
||||
}
|
||||
}
|
||||
|
||||
return proposals.toArray(new ICompletionProposal[proposals.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this descriptor describes an element that can potentially
|
||||
* have children (either sub-elements or text value). If an element can have children,
|
||||
* we want to explicitly write an opening and a separate closing tag.
|
||||
* <p/>
|
||||
* Elements can have children if the descriptor has children element descriptors
|
||||
* or if one of the attributes is a TextValueDescriptor.
|
||||
*
|
||||
* @param descriptor An ElementDescriptor or an AttributeDescriptor
|
||||
* @return True if the descriptor is an ElementDescriptor that can have children or a text value
|
||||
*/
|
||||
private boolean elementCanHaveChildren(Object descriptor) {
|
||||
if (descriptor instanceof ElementDescriptor) {
|
||||
ElementDescriptor desc = (ElementDescriptor) descriptor;
|
||||
if (desc.hasChildren()) {
|
||||
return true;
|
||||
}
|
||||
for (AttributeDescriptor attr_desc : desc.getAttributes()) {
|
||||
if (attr_desc instanceof TextValueDescriptor) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element descriptor matching a given XML node name or null if it can't be
|
||||
* found.
|
||||
* <p/>
|
||||
* This is simplistic; ideally we should consider the parent's chain to make sure we
|
||||
* can differentiate between different hierarchy trees. Right now the first match found
|
||||
* is returned.
|
||||
*/
|
||||
private ElementDescriptor getDescriptor(String nodeName) {
|
||||
return mRootDescriptor.findChildrenDescriptor(nodeName, true /* recursive */);
|
||||
}
|
||||
|
||||
public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the characters which when entered by the user should
|
||||
* automatically trigger the presentation of possible completions.
|
||||
*
|
||||
* In our case, we auto-activate on opening tags and attributes namespace.
|
||||
*
|
||||
* @return the auto activation characters for completion proposal or <code>null</code>
|
||||
* if no auto activation is desired
|
||||
*/
|
||||
public char[] getCompletionProposalAutoActivationCharacters() {
|
||||
return new char[]{ '<', ':', '=' };
|
||||
}
|
||||
|
||||
public char[] getContextInformationAutoActivationCharacters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public IContextInformationValidator getContextInformationValidator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Heuristically extracts the prefix used for determining template relevance
|
||||
* from the viewer's document. The default implementation returns the String from
|
||||
* offset backwards that forms a potential XML element name.
|
||||
*
|
||||
* Code extracted from org.eclipse.jface.text.templatesTemplateCompletionProcessor
|
||||
* and adapted to our needs.
|
||||
*
|
||||
* @param viewer the viewer
|
||||
* @param offset offset into document
|
||||
* @return the prefix to consider
|
||||
*/
|
||||
protected String extractElementPrefix(ITextViewer viewer, int offset) {
|
||||
int i = offset;
|
||||
IDocument document = viewer.getDocument();
|
||||
if (i > document.getLength()) return ""; //$NON-NLS-1$
|
||||
|
||||
try {
|
||||
for (; i > 0; --i) {
|
||||
char ch = document.getChar(i - 1);
|
||||
// accepted characters are a-z and : (for attributes' namespace)
|
||||
if (ch != ':' && (ch < 'a' || ch > 'z')) break;
|
||||
}
|
||||
|
||||
return document.get(i, offset - i);
|
||||
} catch (BadLocationException e) {
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the character at the given offset.
|
||||
* Returns 0 if the offset is invalid.
|
||||
*/
|
||||
protected char extractChar(ITextViewer viewer, int offset) {
|
||||
IDocument document = viewer.getDocument();
|
||||
if (offset > document.getLength()) return 0;
|
||||
|
||||
try {
|
||||
return document.getChar(offset);
|
||||
} catch (BadLocationException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about the current edit of an attribute as reported by parseAttributeInfo.
|
||||
*/
|
||||
private class AttribInfo {
|
||||
/** True if the cursor is located in an attribute's value, false if in an attribute name */
|
||||
public boolean isInValue = false;
|
||||
/** The attribute name. Null when not set. */
|
||||
public String name = null;
|
||||
/** The attribute value. Null when not set. The value *may* start with a quote
|
||||
* (' or "), in which case we know we don't need to quote the string for the user */
|
||||
public String value = null;
|
||||
/** String typed by the user so far (i.e. right before requesting code completion),
|
||||
* which will be corrected if we find a possible completion for an attribute value.
|
||||
* See the long comment in getChoicesForAttribute(). */
|
||||
public String correctedPrefix = null;
|
||||
/** Non-zero if an attribute value need a start/end tag (i.e. quotes or brackets) */
|
||||
public char needTag = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to guess if the cursor is editing an element's name or an attribute following an
|
||||
* element. If it's an attribute, try to find if an attribute name is being defined or
|
||||
* its value.
|
||||
* <br/>
|
||||
* This is currently *only* called when we know the cursor is after a complete element
|
||||
* tag name, so it should never return null.
|
||||
* <br/>
|
||||
* Reference for XML syntax: http://www.w3.org/TR/2006/REC-xml-20060816/#sec-starttags
|
||||
* <br/>
|
||||
* @return An AttribInfo describing which attribute is being edited or null if the cursor is
|
||||
* not editing an attribute (in which case it must be an element's name).
|
||||
*/
|
||||
private AttribInfo parseAttributeInfo(ITextViewer viewer, int offset) {
|
||||
AttribInfo info = new AttribInfo();
|
||||
|
||||
IDocument document = viewer.getDocument();
|
||||
int n = document.getLength();
|
||||
if (offset <= n) {
|
||||
try {
|
||||
n = offset;
|
||||
for (;offset > 0; --offset) {
|
||||
char ch = document.getChar(offset - 1);
|
||||
if (ch == '<') break;
|
||||
}
|
||||
|
||||
// text will contain the full string of the current element,
|
||||
// i.e. whatever is after the "<" to the current cursor
|
||||
String text = document.get(offset, n - offset);
|
||||
|
||||
// Normalize whitespace to single spaces
|
||||
text = sWhitespace.matcher(text).replaceAll(" "); //$NON-NLS-1$
|
||||
|
||||
// Remove the leading element name. By spec, it must be after the < without
|
||||
// any whitespace. If there's nothing left, no attribute has been defined yet.
|
||||
// Be sure to keep any whitespace after the initial word if any, as it matters.
|
||||
text = sFirstElementWord.matcher(text).replaceFirst(""); //$NON-NLS-1$
|
||||
|
||||
// There MUST be space after the element name. If not, the cursor is still
|
||||
// defining the element name.
|
||||
if (!text.startsWith(" ")) { //$NON-NLS-1$
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove full attributes:
|
||||
// Syntax:
|
||||
// name = "..." quoted string with all but < and "
|
||||
// or:
|
||||
// name = '...' quoted string with all but < and '
|
||||
String temp;
|
||||
do {
|
||||
temp = text;
|
||||
text = sFirstAttribute.matcher(temp).replaceFirst(""); //$NON-NLS-1$
|
||||
} while(!temp.equals(text));
|
||||
|
||||
// Now we're left with 3 cases:
|
||||
// - nothing: either there is no attribute definition or the cursor located after
|
||||
// a completed attribute definition.
|
||||
// - a string with no =: the user is writing an attribute name. This case can be
|
||||
// merged with the previous one.
|
||||
// - string with an = sign, optionally followed by a quote (' or "): the user is
|
||||
// writing the value of the attribute.
|
||||
int pos_equal = text.indexOf('=');
|
||||
if (pos_equal == -1) {
|
||||
info.isInValue = false;
|
||||
info.name = text.trim();
|
||||
} else {
|
||||
info.isInValue = true;
|
||||
info.name = text.substring(0, pos_equal).trim();
|
||||
info.value = text.substring(pos_equal + 1).trim();
|
||||
}
|
||||
return info;
|
||||
} catch (BadLocationException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the XML DOM node corresponding to the given offset of the given document.
|
||||
*/
|
||||
protected Node getNode(ITextViewer viewer, int offset) {
|
||||
Node node = null;
|
||||
try {
|
||||
IModelManager mm = StructuredModelManager.getModelManager();
|
||||
if (mm != null) {
|
||||
IStructuredModel model = mm.getExistingModelForRead(viewer.getDocument());
|
||||
if (model != null) {
|
||||
for(; offset >= 0 && node == null; --offset) {
|
||||
node = (Node) model.getIndexedRegion(offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore exceptions.
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the active {@link AndroidEditor} matching this source viewer.
|
||||
*/
|
||||
private AndroidEditor getAndroidEditor(ITextViewer viewer) {
|
||||
IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
|
||||
if (wwin != null) {
|
||||
IWorkbenchPage page = wwin.getActivePage();
|
||||
if (page != null) {
|
||||
IEditorPart editor = page.getActiveEditor();
|
||||
if (editor instanceof AndroidEditor) {
|
||||
ISourceViewer ssviewer = ((AndroidEditor) editor).getStructuredSourceViewer();
|
||||
if (ssviewer == viewer) {
|
||||
return (AndroidEditor) editor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,655 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
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.IStatus;
|
||||
import org.eclipse.core.runtime.QualifiedName;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.jface.dialogs.ErrorDialog;
|
||||
import org.eclipse.jface.text.source.ISourceViewer;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
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.browser.IWorkbenchBrowserSupport;
|
||||
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.wst.sse.core.StructuredModelManager;
|
||||
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
|
||||
import org.eclipse.wst.sse.core.internal.provisional.IModelStateListener;
|
||||
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
|
||||
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
|
||||
import org.eclipse.wst.sse.ui.StructuredTextEditor;
|
||||
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Multi-page form editor for Android XML files.
|
||||
* <p/>
|
||||
* It is designed to work with a {@link StructuredTextEditor} that will display an XML 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 AndroidEditor 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 StructuredTextEditor mEditor;
|
||||
/** Listener for the XML model from the StructuredEditor */
|
||||
private XmlModelStateListener mXmlModelStateListener;
|
||||
|
||||
/**
|
||||
* Creates a form editor.
|
||||
*/
|
||||
public AndroidEditor() {
|
||||
super();
|
||||
ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
|
||||
}
|
||||
|
||||
// ---- Abstract Methods ----
|
||||
|
||||
/**
|
||||
* Returns the root node of the UI element hierarchy manipulated by the current
|
||||
* UI node editor.
|
||||
*/
|
||||
abstract public UiElementNode getUiRootNode();
|
||||
|
||||
/**
|
||||
* Creates the various form pages.
|
||||
* <p/>
|
||||
* Derived classes must implement this to add their own specific tabs.
|
||||
*/
|
||||
abstract protected void createFormPages();
|
||||
|
||||
/**
|
||||
* Subclasses should override this method to process the new XML Model, which XML
|
||||
* root node is given.
|
||||
*
|
||||
* The base implementation is empty.
|
||||
*
|
||||
* @param xml_doc The XML document, if available, or null if none exists.
|
||||
*/
|
||||
protected void xmlModelChanged(Document xml_doc) {
|
||||
// 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() {
|
||||
createFormPages();
|
||||
createTextEditor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(AndroidConstants.EDITORS_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.
|
||||
EditorsPlugin.log(e, "Selecting page '%s' in AndroidEditor 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);
|
||||
|
||||
if (getEditorInput() instanceof IFileEditorInput) {
|
||||
IFile file = ((IFileEditorInput) getEditorInput()).getFile();
|
||||
|
||||
QualifiedName qname = new QualifiedName(AndroidConstants.EDITORS_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)mEditor.getEditorInput())
|
||||
.getFile().getProject().equals(
|
||||
event.getResource())) {
|
||||
IEditorPart editorPart = pages[i].findEditor(mEditor
|
||||
.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() {
|
||||
IStructuredModel xml_model = getModelForRead();
|
||||
if (xml_model != null) {
|
||||
try {
|
||||
if (mXmlModelStateListener != null) {
|
||||
xml_model.removeModelStateListener(mXmlModelStateListener);
|
||||
}
|
||||
|
||||
} finally {
|
||||
xml_model.releaseFromRead();
|
||||
}
|
||||
}
|
||||
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 <a> 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 {
|
||||
mEditor = new StructuredTextEditor();
|
||||
int index = addPage(mEditor, getEditorInput());
|
||||
mTextPageIndex = index;
|
||||
setPageText(index, mEditor.getTitle());
|
||||
|
||||
if (!(mEditor.getTextViewer().getDocument() instanceof IStructuredDocument)) {
|
||||
Status status = new Status(IStatus.ERROR, AndroidConstants.EDITORS_PLUGIN_ID,
|
||||
"Error opening the Android XML editor. Is the document an XML file?");
|
||||
throw new RuntimeException("Android XML Editor Error", new CoreException(status));
|
||||
}
|
||||
|
||||
IStructuredModel xml_model = getModelForRead();
|
||||
if (xml_model != null) {
|
||||
try {
|
||||
mXmlModelStateListener = new XmlModelStateListener();
|
||||
xml_model.addModelStateListener(mXmlModelStateListener);
|
||||
mXmlModelStateListener.modelChanged(xml_model);
|
||||
} finally {
|
||||
xml_model.releaseFromRead();
|
||||
}
|
||||
}
|
||||
} catch (PartInitException e) {
|
||||
ErrorDialog.openError(getSite().getShell(),
|
||||
"Android XML Editor Error", null, e.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ISourceViewer associated with the Structured Text editor.
|
||||
*/
|
||||
public final ISourceViewer getStructuredSourceViewer() {
|
||||
if (mEditor != null) {
|
||||
// We can't access mEditor.getSourceViewer() because it is protected,
|
||||
// however getTextViewer simply returns the SourceViewer casted, so we
|
||||
// can use it instead.
|
||||
return mEditor.getTextViewer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link IStructuredDocument} used by the StructuredTextEditor (aka Source
|
||||
* Editor) or null if not available.
|
||||
*/
|
||||
public final IStructuredDocument getStructuredDocument() {
|
||||
if (mEditor != null && mEditor.getTextViewer() != null) {
|
||||
return (IStructuredDocument) mEditor.getTextViewer().getDocument();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version of the model that has been shared for read.
|
||||
* <p/>
|
||||
* Callers <em>must</em> call model.releaseFromRead() when done, typically
|
||||
* in a try..finally clause.
|
||||
*
|
||||
* @return The model for the XML document or null if cannot be obtained from the editor
|
||||
*/
|
||||
public final IStructuredModel getModelForRead() {
|
||||
IStructuredDocument document = getStructuredDocument();
|
||||
if (document != null) {
|
||||
IModelManager mm = StructuredModelManager.getModelManager();
|
||||
if (mm != null) {
|
||||
return mm.getModelForRead(document);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version of the model that has been shared for edit.
|
||||
* <p/>
|
||||
* Callers <em>must</em> call model.releaseFromEdit() when done, typically
|
||||
* in a try..finally clause.
|
||||
*
|
||||
* @return The model for the XML document or null if cannot be obtained from the editor
|
||||
*/
|
||||
public final IStructuredModel getModelForEdit() {
|
||||
IStructuredDocument document = getStructuredDocument();
|
||||
if (document != null) {
|
||||
IModelManager mm = StructuredModelManager.getModelManager();
|
||||
if (mm != null) {
|
||||
return mm.getModelForEdit(document);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to perform edits on the XML model whilst making sure the
|
||||
* model has been prepared to be changed.
|
||||
*
|
||||
* It first gets a model for edition, then calls aboutToChangeModel, then performs the
|
||||
* requested action and finally calls changedModel and releaseFromEdit.
|
||||
*
|
||||
* The method is synchronous. As soon as the changedModel method is called, XML model
|
||||
* listeners will be triggered.
|
||||
*
|
||||
* @param edit_action Something that will change
|
||||
*/
|
||||
public final void editXmlModel(Runnable edit_action) {
|
||||
IStructuredModel model = getModelForEdit();
|
||||
try {
|
||||
model.aboutToChangeModel();
|
||||
edit_action.run();
|
||||
} finally {
|
||||
// Notify the model we're done modifying it. This must *always* be executed.
|
||||
model.changedModel();
|
||||
model.releaseFromEdit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML {@link Document} or null if we can't get it
|
||||
*/
|
||||
protected final Document getXmlDocument(IStructuredModel model) {
|
||||
if (model == null) {
|
||||
EditorsPlugin.log(IStatus.WARNING, "Android Editor: No XML model for root node."); //$NON-NLS-1$
|
||||
return null;
|
||||
}
|
||||
|
||||
if (model instanceof IDOMModel) {
|
||||
IDOMModel dom_model = (IDOMModel) model;
|
||||
return dom_model.getDocument();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to changes in the underlying XML model in the structured editor.
|
||||
*/
|
||||
private class XmlModelStateListener implements IModelStateListener {
|
||||
|
||||
/**
|
||||
* A model is about to be changed. This typically is initiated by one
|
||||
* client of the model, to signal a large change and/or a change to the
|
||||
* model's ID or base Location. A typical use might be if a client might
|
||||
* want to suspend processing until all changes have been made.
|
||||
* <p/>
|
||||
* This AndroidEditor implementation of IModelChangedListener is empty.
|
||||
*/
|
||||
public void modelAboutToBeChanged(IStructuredModel model) {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that the changes foretold by modelAboutToBeChanged have been
|
||||
* made. A typical use might be to refresh, or to resume processing that
|
||||
* was suspended as a result of modelAboutToBeChanged.
|
||||
* <p/>
|
||||
* This AndroidEditor implementation calls the xmlModelChanged callback.
|
||||
*/
|
||||
public void modelChanged(IStructuredModel model) {
|
||||
xmlModelChanged(getXmlDocument(model));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies that a model's dirty state has changed, and passes that state
|
||||
* in isDirty. A model becomes dirty when any change is made, and becomes
|
||||
* not-dirty when the model is saved.
|
||||
* <p/>
|
||||
* This AndroidEditor implementation of IModelChangedListener is empty.
|
||||
*/
|
||||
public void modelDirtyStateChanged(IStructuredModel model, boolean isDirty) {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* A modelDeleted means the underlying resource has been deleted. The
|
||||
* model itself is not removed from model management until all have
|
||||
* released it. Note: baseLocation is not (necessarily) changed in this
|
||||
* event, but may not be accurate.
|
||||
* <p/>
|
||||
* This AndroidEditor implementation of IModelChangedListener is empty.
|
||||
*/
|
||||
public void modelResourceDeleted(IStructuredModel model) {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* A model has been renamed or copied (as in saveAs..). In the renamed
|
||||
* case, the two paramenters are the same instance, and only contain the
|
||||
* new info for id and base location.
|
||||
* <p/>
|
||||
* This AndroidEditor implementation of IModelChangedListener is empty.
|
||||
*/
|
||||
public void modelResourceMoved(IStructuredModel oldModel, IStructuredModel newModel) {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* This AndroidEditor implementation of IModelChangedListener is empty.
|
||||
*/
|
||||
public void modelAboutToBeReinitialized(IStructuredModel structuredModel) {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* This AndroidEditor implementation of IModelChangedListener is empty.
|
||||
*/
|
||||
public void modelReinitialized(IStructuredModel structuredModel) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors;
|
||||
|
||||
|
||||
import org.eclipse.jface.text.IAutoEditStrategy;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.ITextHover;
|
||||
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
|
||||
import org.eclipse.jface.text.contentassist.IContentAssistant;
|
||||
import org.eclipse.jface.text.formatter.IContentFormatter;
|
||||
import org.eclipse.jface.text.source.ISourceViewer;
|
||||
import org.eclipse.jface.viewers.IInputProvider;
|
||||
import org.eclipse.wst.sse.core.text.IStructuredPartitions;
|
||||
import org.eclipse.wst.xml.core.text.IXMLPartitions;
|
||||
import org.eclipse.wst.xml.ui.StructuredTextViewerConfigurationXML;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Base Source Viewer Configuration for Android resources.
|
||||
*/
|
||||
public class AndroidSourceViewerConfig extends StructuredTextViewerConfigurationXML {
|
||||
|
||||
/** Content Assist Processor to use for all handled partitions. */
|
||||
private IContentAssistProcessor mProcessor;
|
||||
|
||||
public AndroidSourceViewerConfig(IContentAssistProcessor processor) {
|
||||
super();
|
||||
mProcessor = processor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
|
||||
return super.getContentAssistant(sourceViewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content assist processors that will be used for content
|
||||
* assist in the given source viewer and for the given partition type.
|
||||
*
|
||||
* @param sourceViewer the source viewer to be configured by this
|
||||
* configuration
|
||||
* @param partitionType the partition type for which the content assist
|
||||
* processors are applicable
|
||||
* @return IContentAssistProcessors or null if should not be supported
|
||||
*/
|
||||
@Override
|
||||
protected IContentAssistProcessor[] getContentAssistProcessors(
|
||||
ISourceViewer sourceViewer, String partitionType) {
|
||||
ArrayList<IContentAssistProcessor> processors = new ArrayList<IContentAssistProcessor>();
|
||||
if (partitionType == IStructuredPartitions.UNKNOWN_PARTITION ||
|
||||
partitionType == IStructuredPartitions.DEFAULT_PARTITION ||
|
||||
partitionType == IXMLPartitions.XML_DEFAULT) {
|
||||
if (sourceViewer instanceof IInputProvider) {
|
||||
IInputProvider input = (IInputProvider) sourceViewer;
|
||||
Object a = input.getInput();
|
||||
if (a != null)
|
||||
a.toString();
|
||||
}
|
||||
|
||||
IDocument doc = sourceViewer.getDocument();
|
||||
if (doc != null)
|
||||
doc.toString();
|
||||
|
||||
processors.add(mProcessor);
|
||||
}
|
||||
|
||||
IContentAssistProcessor[] others = super.getContentAssistProcessors(sourceViewer,
|
||||
partitionType);
|
||||
if (others != null && others.length > 0) {
|
||||
for (IContentAssistProcessor p : others) {
|
||||
processors.add(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (processors.size() > 0) {
|
||||
return processors.toArray(new IContentAssistProcessor[processors.size()]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {
|
||||
// TODO text hover for android xml
|
||||
return super.getTextHover(sourceViewer, contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAutoEditStrategy[] getAutoEditStrategies(
|
||||
ISourceViewer sourceViewer, String contentType) {
|
||||
// TODO auto edit strategies for android xml
|
||||
return super.getAutoEditStrategies(sourceViewer, contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IContentFormatter getContentFormatter(ISourceViewer sourceViewer) {
|
||||
// TODO content formatter for android xml
|
||||
return super.getContentFormatter(sourceViewer);
|
||||
}
|
||||
}
|
||||
@@ -1,672 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.CommonPlugin;
|
||||
import com.android.ide.eclipse.common.SdkStatsHelper;
|
||||
import com.android.ide.eclipse.common.StreamHelper;
|
||||
import com.android.ide.eclipse.common.resources.FrameworkResourceManager;
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin.LayoutBridge.LoadStatus;
|
||||
import com.android.ide.eclipse.editors.layout.LayoutEditor;
|
||||
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.menu.MenuEditor;
|
||||
import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors;
|
||||
import com.android.ide.eclipse.editors.resources.ResourcesEditor;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ProjectResources;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener;
|
||||
import com.android.ide.eclipse.editors.xml.XmlEditor;
|
||||
import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors;
|
||||
import com.android.layoutlib.api.ILayoutBridge;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IMarkerDelta;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.resources.IWorkspace;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.QualifiedName;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.SubMonitor;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
import org.eclipse.jface.dialogs.MessageDialog;
|
||||
import org.eclipse.jface.resource.ImageDescriptor;
|
||||
import org.eclipse.swt.graphics.Color;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
import org.eclipse.ui.IEditorDescriptor;
|
||||
import org.eclipse.ui.IEditorPart;
|
||||
import org.eclipse.ui.IWorkbench;
|
||||
import org.eclipse.ui.IWorkbenchPage;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
import org.eclipse.ui.console.MessageConsole;
|
||||
import org.eclipse.ui.console.MessageConsoleStream;
|
||||
import org.eclipse.ui.ide.IDE;
|
||||
import org.eclipse.ui.part.FileEditorInput;
|
||||
import org.eclipse.ui.plugin.AbstractUIPlugin;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.Constants;
|
||||
import org.osgi.framework.Version;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* The activator class controls the plug-in life cycle
|
||||
*/
|
||||
public class EditorsPlugin extends AbstractUIPlugin {
|
||||
// The shared instance
|
||||
private static EditorsPlugin sPlugin;
|
||||
|
||||
private static Image sAndroidLogo;
|
||||
private static ImageDescriptor sAndroidLogoDesc;
|
||||
|
||||
private ResourceMonitor mResourceMonitor;
|
||||
private SdkPathChangedListener mSdkPathChangedListener;
|
||||
private ArrayList<Runnable> mResourceRefreshListener = new ArrayList<Runnable>();
|
||||
|
||||
private MessageConsoleStream mAndroidConsoleStream;
|
||||
/** Stream to write error messages to the android console */
|
||||
private MessageConsoleStream mAndroidConsoleErrorStream;
|
||||
|
||||
public final static class LayoutBridge {
|
||||
public enum LoadStatus { LOADING, LOADED, FAILED }
|
||||
|
||||
/** Link to the layout bridge */
|
||||
public ILayoutBridge bridge;
|
||||
|
||||
public LoadStatus status = LoadStatus.LOADING;
|
||||
}
|
||||
|
||||
private final LayoutBridge mLayoutBridge = new LayoutBridge();
|
||||
|
||||
private boolean mLayoutBridgeInit;
|
||||
|
||||
private ClassLoader mBridgeClassLoader;
|
||||
|
||||
private Color mRed;
|
||||
|
||||
|
||||
/**
|
||||
* The constructor
|
||||
*/
|
||||
public EditorsPlugin() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
|
||||
* method refreshes the plug-in actions. Subclasses may extend this method,
|
||||
* but must send super <b>first</b>.
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
|
||||
*/
|
||||
@Override
|
||||
public void start(BundleContext context) throws Exception {
|
||||
super.start(context);
|
||||
sPlugin = this;
|
||||
sAndroidLogoDesc = imageDescriptorFromPlugin(AndroidConstants.EDITORS_PLUGIN_ID,
|
||||
"/icons/android.png"); //$NON-NLS-1$
|
||||
sAndroidLogo = sAndroidLogoDesc.createImage();
|
||||
|
||||
// get the stream to write in the android console.
|
||||
MessageConsole androidConsole = CommonPlugin.getDefault().getAndroidConsole();
|
||||
mAndroidConsoleStream = androidConsole.newMessageStream();
|
||||
|
||||
mAndroidConsoleErrorStream = androidConsole.newMessageStream();
|
||||
mRed = new Color(getDisplay(), 0xFF, 0x00, 0x00);
|
||||
|
||||
// because this can be run, in some cases, by a non ui thread, and beccause
|
||||
// changing the console properties update the ui, we need to make this change
|
||||
// in the ui thread.
|
||||
getDisplay().asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
mAndroidConsoleErrorStream.setColor(mRed);
|
||||
}
|
||||
});
|
||||
|
||||
// Add a resource listener to handle compiled resources.
|
||||
IWorkspace ws = ResourcesPlugin.getWorkspace();
|
||||
mResourceMonitor = ResourceMonitor.startMonitoring(ws);
|
||||
|
||||
if (mResourceMonitor != null) {
|
||||
setupDefaultEditor(mResourceMonitor);
|
||||
ResourceManager.setup(mResourceMonitor);
|
||||
}
|
||||
|
||||
// Setup the sdk location changed listener and invoke it the first time
|
||||
mSdkPathChangedListener = new SdkPathChangedListener();
|
||||
FrameworkResourceManager.getInstance().addFrameworkResourcesChangeListener(
|
||||
mSdkPathChangedListener);
|
||||
|
||||
// ping the usage server
|
||||
pingUsageServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* The <code>AbstractUIPlugin</code> implementation of this <code>Plugin</code>
|
||||
* method saves this plug-in's preference and dialog stores and shuts down
|
||||
* its image registry (if they are in use). Subclasses may extend this
|
||||
* method, but must send super <b>last</b>. A try-finally statement should
|
||||
* be used where necessary to ensure that <code>super.shutdown()</code> is
|
||||
* always done.
|
||||
*
|
||||
* @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
|
||||
*/
|
||||
@Override
|
||||
public void stop(BundleContext context) throws Exception {
|
||||
sPlugin = null;
|
||||
sAndroidLogo.dispose();
|
||||
|
||||
IconFactory.getInstance().Dispose();
|
||||
|
||||
// Remove the resource listener that handles compiled resources.
|
||||
IWorkspace ws = ResourcesPlugin.getWorkspace();
|
||||
ResourceMonitor.stopMonitoring(ws);
|
||||
|
||||
FrameworkResourceManager.getInstance().removeFrameworkResourcesChangeListener(
|
||||
mSdkPathChangedListener);
|
||||
mSdkPathChangedListener = null;
|
||||
|
||||
mRed.dispose();
|
||||
|
||||
super.stop(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shared instance
|
||||
*
|
||||
* @return the shared instance
|
||||
*/
|
||||
public static EditorsPlugin getDefault() {
|
||||
return sPlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Image for the small Android logo.
|
||||
*
|
||||
* Callers should not dispose it.
|
||||
*/
|
||||
public static Image getAndroidLogo() {
|
||||
return sAndroidLogo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link ImageDescriptor} for the small Android logo.
|
||||
*
|
||||
* Callers should not dispose it.
|
||||
*/
|
||||
public static ImageDescriptor getAndroidLogoDesc() {
|
||||
return sAndroidLogoDesc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message to the default Eclipse log.
|
||||
*
|
||||
* @param severity One of IStatus' severity codes: OK, ERROR, INFO, WARNING or CANCEL.
|
||||
* @param format The format string, like for String.format().
|
||||
* @param args The arguments for the format string, like for String.format().
|
||||
*/
|
||||
public static void log(int severity, String format, Object ... args) {
|
||||
String message = String.format(format, args);
|
||||
Status status = new Status(severity, AndroidConstants.EDITORS_PLUGIN_ID, message);
|
||||
getDefault().getLog().log(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an exception to the default Eclipse log.
|
||||
* <p/>
|
||||
* The status severity is always set to ERROR.
|
||||
*
|
||||
* @param exception The exception to log. Its call trace will be recorded.
|
||||
* @param format The format string, like for String.format().
|
||||
* @param args The arguments for the format string, like for String.format().
|
||||
*/
|
||||
public static void log(Throwable exception, String format, Object ... args) {
|
||||
String message = String.format(format, args);
|
||||
Status status = new Status(IStatus.ERROR, AndroidConstants.EDITORS_PLUGIN_ID,
|
||||
message, exception);
|
||||
getDefault().getLog().log(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ResourceMonitor object.
|
||||
*/
|
||||
public ResourceMonitor getResourceMonitor() {
|
||||
return mResourceMonitor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up the editor to register default editors for resource files when needed.
|
||||
*
|
||||
* This is called by the {@link EditorsPlugin} during initialization.
|
||||
*
|
||||
* @param monitor The main Resource Monitor object.
|
||||
*/
|
||||
public void setupDefaultEditor(ResourceMonitor monitor) {
|
||||
monitor.addFileListener(new IFileListener() {
|
||||
|
||||
private static final String UNKNOWN_EDITOR = "unknown-editor"; //$NON-NLS-1$
|
||||
|
||||
/* (non-Javadoc)
|
||||
* Sent when a file changed.
|
||||
* @param file The file that changed.
|
||||
* @param markerDeltas The marker deltas for the file.
|
||||
* @param kind The change kind. This is equivalent to
|
||||
* {@link IResourceDelta#accept(IResourceDeltaVisitor)}
|
||||
*
|
||||
* @see IFileListener#fileChanged
|
||||
*/
|
||||
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
|
||||
if (AndroidConstants.EXT_XML.equals(file.getFileExtension())) {
|
||||
// The resources files must have a file path similar to
|
||||
// project/res/.../*.xml
|
||||
// There is no support for sub folders, so the segment count must be 4
|
||||
if (file.getFullPath().segmentCount() == 4) {
|
||||
// check if we are inside the res folder.
|
||||
String segment = file.getFullPath().segment(1);
|
||||
if (segment.equalsIgnoreCase(AndroidConstants.FD_RESOURCES)) {
|
||||
// we are inside a res/ folder, get the actual ResourceFolder
|
||||
ProjectResources resources = ResourceManager.getInstance().
|
||||
getProjectResources(file.getProject());
|
||||
|
||||
// This happens when importing old Android projects in Eclipse
|
||||
// that lack the container (probably because resources fail to build
|
||||
// properly.)
|
||||
if (resources == null) {
|
||||
log(IStatus.INFO,
|
||||
"getProjectResources failed for path %1$s in project %2$s", //$NON-NLS-1$
|
||||
file.getFullPath().toOSString(),
|
||||
file.getProject().getName());
|
||||
return;
|
||||
}
|
||||
|
||||
ResourceFolder resFolder = resources.getResourceFolder(
|
||||
(IFolder)file.getParent());
|
||||
|
||||
if (resFolder != null) {
|
||||
if (kind == IResourceDelta.ADDED) {
|
||||
resourceAdded(file, resFolder.getType());
|
||||
} else if (kind == IResourceDelta.CHANGED) {
|
||||
resourceChanged(file, resFolder.getType());
|
||||
}
|
||||
} else {
|
||||
// if the res folder is null, this means the name is invalid,
|
||||
// in this case we remove whatever android editors that was set
|
||||
// as the default editor.
|
||||
IEditorDescriptor desc = IDE.getDefaultEditor(file);
|
||||
String editorId = desc.getId();
|
||||
if (editorId.startsWith(AndroidConstants.EDITORS_PLUGIN_ID)) {
|
||||
// reset the default editor.
|
||||
IDE.setDefaultEditor(file, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resourceAdded(IFile file, ResourceFolderType type) {
|
||||
// set the default editor based on the type.
|
||||
if (type == ResourceFolderType.LAYOUT) {
|
||||
IDE.setDefaultEditor(file, LayoutEditor.ID);
|
||||
} else if (type == ResourceFolderType.DRAWABLE
|
||||
|| type == ResourceFolderType.VALUES) {
|
||||
IDE.setDefaultEditor(file, ResourcesEditor.ID);
|
||||
} else if (type == ResourceFolderType.MENU) {
|
||||
IDE.setDefaultEditor(file, MenuEditor.ID);
|
||||
} else if (type == ResourceFolderType.XML) {
|
||||
if (XmlEditor.canHandleFile(file)) {
|
||||
IDE.setDefaultEditor(file, XmlEditor.ID);
|
||||
} else {
|
||||
// set a property to determine later if the XML can be handled
|
||||
QualifiedName qname = new QualifiedName(
|
||||
AndroidConstants.EDITORS_PLUGIN_ID,
|
||||
UNKNOWN_EDITOR);
|
||||
try {
|
||||
file.setPersistentProperty(qname, "1");
|
||||
} catch (CoreException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resourceChanged(IFile file, ResourceFolderType type) {
|
||||
if (type == ResourceFolderType.XML) {
|
||||
IEditorDescriptor ed = IDE.getDefaultEditor(file);
|
||||
if (ed == null || ed.getId() != XmlEditor.ID) {
|
||||
QualifiedName qname = new QualifiedName(
|
||||
AndroidConstants.EDITORS_PLUGIN_ID,
|
||||
UNKNOWN_EDITOR);
|
||||
String prop = null;
|
||||
try {
|
||||
prop = file.getPersistentProperty(qname);
|
||||
} catch (CoreException e) {
|
||||
// pass
|
||||
}
|
||||
if (prop != null && XmlEditor.canHandleFile(file)) {
|
||||
try {
|
||||
// remove the property & set editor
|
||||
file.setPersistentProperty(qname, null);
|
||||
IWorkbenchPage page = PlatformUI.getWorkbench().
|
||||
getActiveWorkbenchWindow().getActivePage();
|
||||
|
||||
IEditorPart oldEditor = page.findEditor(new FileEditorInput(file));
|
||||
if (oldEditor != null &&
|
||||
CommonPlugin.displayPrompt("Android XML Editor",
|
||||
String.format("The file you just saved as been recognized as a file that could be better handled using the Android XML Editor. Do you want to edit '%1$s' using the Android XML editor instead?",
|
||||
file.getFullPath()))) {
|
||||
IDE.setDefaultEditor(file, XmlEditor.ID);
|
||||
IEditorPart newEditor = page.openEditor(
|
||||
new FileEditorInput(file),
|
||||
XmlEditor.ID,
|
||||
true, /* activate */
|
||||
IWorkbenchPage.MATCH_NONE);
|
||||
|
||||
if (newEditor != null) {
|
||||
page.closeEditor(oldEditor, true /* save */);
|
||||
}
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
// setPersistentProperty or page.openEditor may have failed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, IResourceDelta.ADDED | IResourceDelta.CHANGED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to notifications from the resource manager than the SDK resources have been updated.
|
||||
* It gets the new resources from the {@link FrameworkResourceManager} and then try to update
|
||||
* the layout descriptors from the new layout data.
|
||||
*/
|
||||
private class SdkPathChangedListener implements Runnable {
|
||||
public void run() {
|
||||
|
||||
// Perform the update in a thread (here an Eclipse runtime job)
|
||||
// since this should never block the caller (especially the start method)
|
||||
new Job("Editors: Load Framework Resource Parser") {
|
||||
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
try {
|
||||
SubMonitor progress = SubMonitor.convert(monitor, "Update Description",
|
||||
60);
|
||||
|
||||
FrameworkResourceManager resourceManager = FrameworkResourceManager.getInstance();
|
||||
|
||||
AndroidManifestDescriptors.updateDescriptors(
|
||||
resourceManager.getManifestDefinitions());
|
||||
progress.worked(10);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
LayoutDescriptors.getInstance().updateDescriptors(
|
||||
resourceManager.getLayoutViewsInfo(),
|
||||
resourceManager.getLayoutGroupsInfo());
|
||||
progress.worked(10);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
MenuDescriptors.getInstance().updateDescriptors(
|
||||
resourceManager.getXmlMenuDefinitions());
|
||||
progress.worked(10);
|
||||
|
||||
if (progress.isCanceled()) {
|
||||
return Status.CANCEL_STATUS;
|
||||
}
|
||||
|
||||
XmlDescriptors.getInstance().updateDescriptors(
|
||||
resourceManager.getXmlSearchableDefinitions(),
|
||||
resourceManager.getPreferencesInfo(),
|
||||
resourceManager.getPreferenceGroupsInfo());
|
||||
progress.worked(10);
|
||||
|
||||
// load the layout lib bridge.
|
||||
if (System.getenv("ANDROID_DISABLE_LAYOUT") == null) {
|
||||
loadLayoutBridge();
|
||||
FrameworkResourceManager frMgr = FrameworkResourceManager.getInstance();
|
||||
ResourceManager rMgr = ResourceManager.getInstance();
|
||||
rMgr.loadFrameworkResources(frMgr.getFrameworkResourcesLocation());
|
||||
}
|
||||
progress.worked(10);
|
||||
|
||||
// Notify resource changed listeners
|
||||
progress.subTask("Refresh UI");
|
||||
progress.setWorkRemaining(mResourceRefreshListener.size());
|
||||
|
||||
// Clone the list before iterating, to avoid Concurrent Modification
|
||||
// exceptions
|
||||
@SuppressWarnings("unchecked")
|
||||
ArrayList<Runnable> listeners = (ArrayList<Runnable>)
|
||||
mResourceRefreshListener.clone();
|
||||
for (Runnable listener : listeners) {
|
||||
try {
|
||||
getDisplay().syncExec(listener);
|
||||
} catch (Exception e) {
|
||||
log(e, "ResourceRefreshListener Failed"); //$NON-NLS-1$
|
||||
} finally {
|
||||
progress.worked(1);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
EditorsPlugin.log(e, "Load Framework Resource Parser failed"); //$NON-NLS-1$
|
||||
EditorsPlugin.printToConsole("Framework Resource Parser",
|
||||
"Failed to parse");
|
||||
} finally {
|
||||
if (monitor != null) {
|
||||
monitor.done();
|
||||
}
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
}.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
public void addResourceChangedListener(Runnable resourceRefreshListener) {
|
||||
mResourceRefreshListener.add(resourceRefreshListener);
|
||||
}
|
||||
|
||||
public void removeResourceChangedListener(Runnable resourceRefreshListener) {
|
||||
mResourceRefreshListener.remove(resourceRefreshListener);
|
||||
}
|
||||
|
||||
public static Display getDisplay() {
|
||||
IWorkbench bench = sPlugin.getWorkbench();
|
||||
if (bench != null) {
|
||||
return bench.getDisplay();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings the usage start server.
|
||||
*/
|
||||
private void pingUsageServer() {
|
||||
// In order to not block the plugin loading, so we spawn another thread.
|
||||
new Thread("Ping!") { //$NON-NLS-1$
|
||||
@Override
|
||||
public void run() {
|
||||
// get the version of the plugin
|
||||
String versionString = (String) getBundle().getHeaders().get(
|
||||
Constants.BUNDLE_VERSION);
|
||||
Version version = new Version(versionString);
|
||||
|
||||
SdkStatsHelper.pingUsageServer("editors", version); //$NON-NLS-1$
|
||||
}
|
||||
}.start();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints one or more message to the android console.
|
||||
* @param tag The tag to be associated with the message. Can be null.
|
||||
* @param objects the objects to print through their <code>toString</code> method.
|
||||
*/
|
||||
public static synchronized void printToConsole(String tag, Object... objects) {
|
||||
StreamHelper.printToStream(sPlugin.mAndroidConsoleStream, tag, objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints one or more error messages to the android console.
|
||||
* @param tag The tag to be associated with the message. Can be null.
|
||||
* @param objects the objects to print through their <code>toString</code> method.
|
||||
*/
|
||||
public static synchronized void printErrorToConsole(String tag, Object... objects) {
|
||||
StreamHelper.printToStream(sPlugin.mAndroidConsoleErrorStream, tag, objects);
|
||||
}
|
||||
|
||||
public static synchronized OutputStream getErrorStream() {
|
||||
return sPlugin.mAndroidConsoleErrorStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an error dialog box. This dialog box is ran asynchronously in the ui thread,
|
||||
* therefore this method can be called from any thread.
|
||||
* @param title The title of the dialog box
|
||||
* @param message The error message
|
||||
*/
|
||||
public final static void displayError(final String title, final String message) {
|
||||
// get the current Display
|
||||
final Display display = getDisplay();
|
||||
|
||||
// dialog box only run in ui thread..
|
||||
display.asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
Shell shell = display.getActiveShell();
|
||||
MessageDialog.openError(shell, title, message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a yes/no question dialog box. This dialog is opened synchronously in the ui thread,
|
||||
* therefore this message can be called from any thread.
|
||||
* @param title The title of the dialog box
|
||||
* @param message The error message
|
||||
* @return true if OK was clicked.
|
||||
*/
|
||||
public final static boolean displayPrompt(final String title, final String message) {
|
||||
// get the current Display and Shell
|
||||
final Display display = getDisplay();
|
||||
|
||||
// we need to ask the user what he wants to do.
|
||||
final boolean[] wrapper = new boolean[] { false };
|
||||
display.syncExec(new Runnable() {
|
||||
public void run() {
|
||||
Shell shell = display.getActiveShell();
|
||||
wrapper[0] = MessageDialog.openQuestion(shell, title, message);
|
||||
}
|
||||
});
|
||||
return wrapper[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link LayoutBridge} object possibly containing a {@link ILayoutBridge} object.
|
||||
* <p/>If {@link LayoutBridge#bridge} is <code>null</code>, {@link LayoutBridge#status} will
|
||||
* contain the reason (either {@link LoadStatus#LOADING} or {@link LoadStatus#FAILED}).
|
||||
* <p/>Valid {@link ILayoutBridge} objects are always initialized before being returned.
|
||||
*/
|
||||
public synchronized LayoutBridge getLayoutBridge() {
|
||||
if (mLayoutBridgeInit == false && mLayoutBridge.bridge != null) {
|
||||
FrameworkResourceManager manager = FrameworkResourceManager.getInstance();
|
||||
mLayoutBridge.bridge.init(
|
||||
manager.getFrameworkFontLocation() + AndroidConstants.FD_DEFAULT_RES,
|
||||
manager.getEnumValueMap());
|
||||
mLayoutBridgeInit = true;
|
||||
}
|
||||
return mLayoutBridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the layout bridge from the dynamically loaded layoutlib.jar
|
||||
*/
|
||||
private void loadLayoutBridge() {
|
||||
try {
|
||||
// reset to be sure we won't be using an obsolete version if we
|
||||
// get an exception somewhere.
|
||||
mLayoutBridge.bridge = null;
|
||||
mLayoutBridge.status = LayoutBridge.LoadStatus.LOADING;
|
||||
|
||||
// get the URL for the file.
|
||||
File f = new File(
|
||||
FrameworkResourceManager.getInstance().getLayoutLibLocation());
|
||||
if (f.isFile() == false) {
|
||||
log(IStatus.ERROR, "layoutlib.jar is missing!"); //$NON-NLS-1$
|
||||
} else {
|
||||
URL url = f.toURL();
|
||||
|
||||
// create a class loader. Because this jar reference interfaces
|
||||
// that are in the editors plugin, it's important to provide
|
||||
// a parent class loader.
|
||||
mBridgeClassLoader = new URLClassLoader(new URL[] { url },
|
||||
this.getClass().getClassLoader());
|
||||
|
||||
// load the class
|
||||
Class<?> clazz = mBridgeClassLoader.loadClass(AndroidConstants.CLASS_BRIDGE);
|
||||
if (clazz != null) {
|
||||
// instantiate an object of the class.
|
||||
Constructor<?> constructor = clazz.getConstructor();
|
||||
if (constructor != null) {
|
||||
Object bridge = constructor.newInstance();
|
||||
if (bridge instanceof ILayoutBridge) {
|
||||
mLayoutBridge.bridge = (ILayoutBridge)bridge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mLayoutBridge.bridge == null) {
|
||||
mLayoutBridge.status = LayoutBridge.LoadStatus.FAILED;
|
||||
log(IStatus.ERROR, "Failed to load " + AndroidConstants.CLASS_BRIDGE); //$NON-NLS-1$
|
||||
} else {
|
||||
mLayoutBridge.status = LayoutBridge.LoadStatus.LOADED;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
mLayoutBridge.status = LayoutBridge.LoadStatus.FAILED;
|
||||
// log the error.
|
||||
log(t, "Failed to load the LayoutLib");
|
||||
}
|
||||
}
|
||||
|
||||
public ClassLoader getLayoutlibBridgeClassLoader() {
|
||||
return mBridgeClassLoader;
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
/**
|
||||
* Quickly parses a (potential) XML file to extract its first element (i.e. the root element)
|
||||
* and namespace, if any.
|
||||
* <p/>
|
||||
* This is used to determine if a file is an XML document that the XmlEditor can process.
|
||||
* <p/>
|
||||
* TODO use this to remove the hardcoded "android" namespace prefix limitation.
|
||||
*/
|
||||
public final class FirstElementParser {
|
||||
|
||||
private static SAXParserFactory sSaxfactory;
|
||||
|
||||
/**
|
||||
* Result from the XML parsing. <br/>
|
||||
* Contains the name of the root XML element. <br/>
|
||||
* If an XMLNS URI was specified and found, the XMLNS prefix is recorded. Otherwise it is null.
|
||||
*/
|
||||
public static final class Result {
|
||||
private String mElement;
|
||||
private String mXmlnsPrefix;
|
||||
private String mXmlnsUri;
|
||||
|
||||
public String getElement() {
|
||||
return mElement;
|
||||
}
|
||||
|
||||
public String getXmlnsPrefix() {
|
||||
return mXmlnsPrefix;
|
||||
}
|
||||
|
||||
public String getXmlnsUri() {
|
||||
return mXmlnsUri;
|
||||
}
|
||||
|
||||
void setElement(String element) {
|
||||
mElement = element;
|
||||
}
|
||||
|
||||
void setXmlnsPrefix(String xmlnsPrefix) {
|
||||
mXmlnsPrefix = xmlnsPrefix;
|
||||
}
|
||||
|
||||
void setXmlnsUri(String xmlnsUri) {
|
||||
mXmlnsUri = xmlnsUri;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ResultFoundException extends SAXException { }
|
||||
|
||||
/**
|
||||
* Parses the given filename.
|
||||
*
|
||||
* @param osFilename The file to parse.
|
||||
* @param xmlnsUri An optional URL of which we want to know the prefix.
|
||||
* @return The element details found or null if not found.
|
||||
*/
|
||||
public static Result parse(String osFilename, String xmlnsUri) {
|
||||
if (sSaxfactory == null) {
|
||||
// TODO just create a single factory in CommonPlugin and reuse it
|
||||
sSaxfactory = SAXParserFactory.newInstance();
|
||||
sSaxfactory.setNamespaceAware(true);
|
||||
}
|
||||
|
||||
Result result = new Result();
|
||||
if (xmlnsUri != null && xmlnsUri.length() > 0) {
|
||||
result.setXmlnsUri(xmlnsUri);
|
||||
}
|
||||
|
||||
try {
|
||||
SAXParser parser = sSaxfactory.newSAXParser();
|
||||
XmlHandler handler = new XmlHandler(result);
|
||||
parser.parse(new InputSource(new FileReader(osFilename)), handler);
|
||||
|
||||
} catch(ResultFoundException e) {
|
||||
// XML handling was aborted because the required element was found.
|
||||
// Simply return the result.
|
||||
return result;
|
||||
} catch (ParserConfigurationException e) {
|
||||
} catch (SAXException e) {
|
||||
} catch (FileNotFoundException e) {
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor. Use the static parse() method instead.
|
||||
*/
|
||||
private FirstElementParser() {
|
||||
// pass
|
||||
}
|
||||
|
||||
/**
|
||||
* A specialized SAX handler that captures the arguments of the very first element
|
||||
* (i.e. the root element)
|
||||
*/
|
||||
private static class XmlHandler extends DefaultHandler {
|
||||
private final Result mResult;
|
||||
|
||||
public XmlHandler(Result result) {
|
||||
mResult = result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a namespace prefix mapping.
|
||||
* I.e. for xmlns:android="some-uri", this received prefix="android" and uri="some-uri".
|
||||
* <p/>
|
||||
* The prefix is recorded in the result structure if the URI is the one searched for.
|
||||
* <p/>
|
||||
* This event happens <em>before</em> the corresponding startElement event.
|
||||
*/
|
||||
@Override
|
||||
public void startPrefixMapping(String prefix, String uri) {
|
||||
if (uri.equals(mResult.getXmlnsUri())) {
|
||||
mResult.setXmlnsPrefix(prefix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a new element start.
|
||||
* <p/>
|
||||
* This simply records the element name and abort processing by throwing an exception.
|
||||
*/
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String name, Attributes attributes)
|
||||
throws SAXException {
|
||||
mResult.setElement(localName);
|
||||
throw new ResultFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.editors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
|
||||
import org.eclipse.jface.resource.ImageDescriptor;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.graphics.Font;
|
||||
import org.eclipse.swt.graphics.FontData;
|
||||
import org.eclipse.swt.graphics.GC;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.swt.graphics.ImageData;
|
||||
import org.eclipse.swt.graphics.Point;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Factory to generate icons for Android Editors.
|
||||
* <p/>
|
||||
* Icons are kept here and reused.
|
||||
*/
|
||||
public class IconFactory {
|
||||
|
||||
public static final int COLOR_RED = SWT.COLOR_DARK_RED;
|
||||
public static final int COLOR_GREEN = SWT.COLOR_DARK_GREEN;
|
||||
public static final int COLOR_BLUE = SWT.COLOR_DARK_BLUE;
|
||||
public static final int COLOR_DEFAULT = SWT.COLOR_BLACK;
|
||||
|
||||
public static final int SHAPE_CIRCLE = 'C';
|
||||
public static final int SHAPE_RECT = 'R';
|
||||
public static final int SHAPE_DEFAULT = SHAPE_CIRCLE;
|
||||
|
||||
private static IconFactory sInstance;
|
||||
|
||||
private HashMap<String, Image> mIconMap = new HashMap<String, Image>();
|
||||
private HashMap<String, ImageDescriptor> mImageDescMap = new HashMap<String, ImageDescriptor>();
|
||||
|
||||
private IconFactory() {
|
||||
}
|
||||
|
||||
public static synchronized IconFactory getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new IconFactory();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
// Dispose icons
|
||||
for (Image icon : mIconMap.values()) {
|
||||
// The map can contain null values
|
||||
if (icon != null) {
|
||||
icon.dispose();
|
||||
}
|
||||
}
|
||||
mIconMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Image for a given icon name.
|
||||
* <p/>
|
||||
* Callers should not dispose it.
|
||||
*
|
||||
* @param osName The leaf name, without the extension, of an existing icon in the
|
||||
* editor's "icons" directory. If it doesn't exists, a default icon will be
|
||||
* generated automatically based on the name.
|
||||
*/
|
||||
public Image getIcon(String osName) {
|
||||
return getIcon(osName, COLOR_DEFAULT, SHAPE_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Image for a given icon name.
|
||||
* <p/>
|
||||
* Callers should not dispose it.
|
||||
*
|
||||
* @param osName The leaf name, without the extension, of an existing icon in the
|
||||
* editor's "icons" directory. If it doesn't exists, a default icon will be
|
||||
* generated automatically based on the name.
|
||||
* @param color The color of the text in the automatically generated icons,
|
||||
* one of COLOR_DEFAULT, COLOR_RED, COLOR_BLUE or COLOR_RED.
|
||||
* @param shape The shape of the icon in the automatically generated icons,
|
||||
* one of SHAPE_DEFAULT, SHAPE_CIRCLE or SHAPE_RECT.
|
||||
*/
|
||||
public Image getIcon(String osName, int color, int shape) {
|
||||
EditorsPlugin plugin = EditorsPlugin.getDefault();
|
||||
|
||||
String key = Character.toString((char) shape) + Integer.toString(color) + osName;
|
||||
Image icon = mIconMap.get(key);
|
||||
if (icon == null && !mIconMap.containsKey(key)) {
|
||||
ImageDescriptor id = getImageDescriptor(osName, color, shape);
|
||||
if (id != null) {
|
||||
icon = id.createImage();
|
||||
}
|
||||
// Note that we store null references in the icon map, to avoid looking them
|
||||
// up every time. If it didn't exist once, it will not exist later.
|
||||
mIconMap.put(key, icon);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ImageDescriptor for a given icon name.
|
||||
* <p/>
|
||||
* Callers should not dispose it.
|
||||
*
|
||||
* @param osName The leaf name, without the extension, of an existing icon in the
|
||||
* editor's "icons" directory. If it doesn't exists, a default icon will be
|
||||
* generated automatically based on the name.
|
||||
*/
|
||||
public ImageDescriptor getImageDescriptor(String osName) {
|
||||
return getImageDescriptor(osName, COLOR_DEFAULT, SHAPE_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ImageDescriptor for a given icon name.
|
||||
* <p/>
|
||||
* Callers should not dispose it.
|
||||
*
|
||||
* @param osName The leaf name, without the extension, of an existing icon in the
|
||||
* editor's "icons" directory. If it doesn't exists, a default icon will be
|
||||
* generated automatically based on the name.
|
||||
* @param color The color of the text in the automatically generated icons.
|
||||
* one of COLOR_DEFAULT, COLOR_RED, COLOR_BLUE or COLOR_RED.
|
||||
* @param shape The shape of the icon in the automatically generated icons,
|
||||
* one of SHAPE_DEFAULT, SHAPE_CIRCLE or SHAPE_RECT.
|
||||
*/
|
||||
public ImageDescriptor getImageDescriptor(String osName, int color, int shape) {
|
||||
EditorsPlugin plugin = EditorsPlugin.getDefault();
|
||||
|
||||
String key = Character.toString((char) shape) + Integer.toString(color) + osName;
|
||||
ImageDescriptor id = mImageDescMap.get(key);
|
||||
if (id == null && !mImageDescMap.containsKey(key)) {
|
||||
id = plugin.imageDescriptorFromPlugin(
|
||||
AndroidConstants.EDITORS_PLUGIN_ID,
|
||||
String.format("/icons/%1$s.png", osName)); //$NON-NLS-1$
|
||||
|
||||
if (id == null) {
|
||||
id = new LetterImageDescriptor(osName.charAt(0), color, shape);
|
||||
}
|
||||
|
||||
// Note that we store null references in the icon map, to avoid looking them
|
||||
// up every time. If it didn't exist once, it will not exist later.
|
||||
mImageDescMap.put(key, id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple image description that generates a 16x16 image which consists
|
||||
* of a colored letter inside a black & white circle.
|
||||
*/
|
||||
private static class LetterImageDescriptor extends ImageDescriptor {
|
||||
|
||||
private final char mLetter;
|
||||
private final int mColor;
|
||||
private final int mShape;
|
||||
|
||||
public LetterImageDescriptor(char letter, int color, int shape) {
|
||||
mLetter = letter;
|
||||
mColor = color;
|
||||
mShape = shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageData getImageData() {
|
||||
|
||||
final int SX = 15;
|
||||
final int SY = 15;
|
||||
final int RX = 4;
|
||||
final int RY = 4;
|
||||
|
||||
Display display = Display.getCurrent();
|
||||
if (display == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Image image = new Image(display, SX, SY);
|
||||
|
||||
image.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
|
||||
|
||||
GC gc = new GC(image);
|
||||
gc.setAdvanced(true);
|
||||
gc.setAntialias(SWT.ON);
|
||||
gc.setTextAntialias(SWT.ON);
|
||||
|
||||
gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
|
||||
if (mShape == SHAPE_CIRCLE) {
|
||||
gc.fillOval(0, 0, SX - 1, SY - 1);
|
||||
} else if (mShape == SHAPE_RECT) {
|
||||
gc.fillRoundRectangle(0, 0, SX - 1, SY - 1, RX, RY);
|
||||
}
|
||||
|
||||
gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
|
||||
gc.setLineWidth(1);
|
||||
if (mShape == SHAPE_CIRCLE) {
|
||||
gc.drawOval(0, 0, SX - 1, SY - 1);
|
||||
} else if (mShape == SHAPE_RECT) {
|
||||
gc.drawRoundRectangle(0, 0, SX - 1, SY - 1, RX, RY);
|
||||
}
|
||||
|
||||
// Get a bold version of the default system font, if possible.
|
||||
Font font = display.getSystemFont();
|
||||
FontData[] fds = font.getFontData();
|
||||
fds[0].setStyle(SWT.BOLD);
|
||||
// use 3/4th of the circle diameter for the font size (in pixels)
|
||||
// and convert it to "font points" (font points in SWT are hardcoded in an
|
||||
// arbitrary 72 dpi and then converted in real pixels using whatever is
|
||||
// indicated by getDPI -- at least that's how it works under Win32).
|
||||
fds[0].setHeight((int) ((SY + 1) * 3./4. * 72./display.getDPI().y));
|
||||
// Note: win32 implementation always uses fds[0] so we change just that one.
|
||||
// getFontData indicates that the array of fd is really an unusual thing for X11.
|
||||
font = new Font(display, fds);
|
||||
gc.setFont(font);
|
||||
gc.setForeground(display.getSystemColor(mColor));
|
||||
|
||||
// Text measurement varies so slightly depending on the platform
|
||||
int ofx = 0;
|
||||
int ofy = 0;
|
||||
if (AndroidConstants.CURRENT_PLATFORM == AndroidConstants.PLATFORM_WINDOWS) {
|
||||
ofx = +1;
|
||||
ofy = -1;
|
||||
}
|
||||
|
||||
String s = Character.toString(mLetter).toUpperCase();
|
||||
Point p = gc.textExtent(s);
|
||||
int tx = (SX + ofx - p.x) / 2;
|
||||
int ty = (SY + ofy - p.y) / 2;
|
||||
gc.drawText(s, tx, ty, true /* isTransparent */);
|
||||
|
||||
font.dispose();
|
||||
gc.dispose();
|
||||
|
||||
ImageData data = image.getImageData();
|
||||
image.dispose();
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.IconFactory;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
|
||||
/**
|
||||
* {@link AttributeDescriptor} describes an XML attribute with its XML attribute name.
|
||||
* <p/>
|
||||
* An attribute descriptor also knows which UI node should be instantiated to represent
|
||||
* this particular attribute (e.g. text field, icon chooser, class selector, etc.)
|
||||
* Some attributes may be hidden and have no user interface at all.
|
||||
* <p/>
|
||||
* This is an abstract class. Derived classes must implement data description and return
|
||||
* the correct UiAttributeNode-derived class.
|
||||
*/
|
||||
public abstract class AttributeDescriptor {
|
||||
private String mXmlLocalName;
|
||||
private ElementDescriptor mParent;
|
||||
private final String mNsUri;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AttributeDescriptor}
|
||||
*
|
||||
* @param xmlLocalName The XML name of the attribute (case sensitive)
|
||||
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
|
||||
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
|
||||
*/
|
||||
public AttributeDescriptor(String xmlLocalName, String nsUri) {
|
||||
mXmlLocalName = xmlLocalName;
|
||||
mNsUri = nsUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML local name of the attribute (case sensitive)
|
||||
*/
|
||||
public final String getXmlLocalName() {
|
||||
return mXmlLocalName;
|
||||
}
|
||||
|
||||
public final String getNamespaceUri() {
|
||||
return mNsUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML qualified name of the attribute (case sensitive, with namespace prefix
|
||||
* if present)
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
private final String getXmlName() {
|
||||
return mXmlLocalName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace of the attribute.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
private final String getNamespace() {
|
||||
// For now we hard-code the prefix as being "android"
|
||||
if (mXmlLocalName.startsWith("android:")) { //$NON-NLs-1$
|
||||
return AndroidConstants.NS_RESOURCES;
|
||||
}
|
||||
|
||||
return ""; //$NON-NLs-1$
|
||||
}
|
||||
|
||||
final void setParent(ElementDescriptor parent) {
|
||||
mParent = parent;
|
||||
}
|
||||
|
||||
public final ElementDescriptor getParent() {
|
||||
return mParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an optional icon for the attribute.
|
||||
* <p/>
|
||||
* By default this tries to return an icon based on the XML name of the attribute.
|
||||
* If this fails, it tries to return the default Android logo as defined in the
|
||||
* plugin. If all fails, it returns null.
|
||||
*
|
||||
* @return An icon for this element or null.
|
||||
*/
|
||||
public Image getIcon() {
|
||||
IconFactory factory = IconFactory.getInstance();
|
||||
Image icon;
|
||||
icon = factory.getIcon(getXmlLocalName(), IconFactory.COLOR_RED, IconFactory.SHAPE_CIRCLE);
|
||||
return icon != null ? icon : EditorsPlugin.getAndroidLogo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uiParent The {@link UiElementNode} parent of this UI attribute.
|
||||
* @return A new {@link UiAttributeNode} linked to this descriptor or null if this
|
||||
* attribute has no user interface.
|
||||
*/
|
||||
public abstract UiAttributeNode createUiNode(UiElementNode uiParent);
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAbstractTextAttributeNode;
|
||||
|
||||
import org.eclipse.jface.viewers.ILabelProvider;
|
||||
import org.eclipse.jface.viewers.ILabelProviderListener;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
|
||||
/**
|
||||
* Label provider for {@link UiAbstractTextAttributeNode}.
|
||||
*/
|
||||
public class AttributeDescriptorLabelProvider implements ILabelProvider {
|
||||
|
||||
private final static AttributeDescriptorLabelProvider sThis =
|
||||
new AttributeDescriptorLabelProvider();
|
||||
|
||||
public static ILabelProvider getProvider() {
|
||||
return sThis;
|
||||
}
|
||||
|
||||
public Image getImage(Object element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getText(Object element) {
|
||||
if (element instanceof UiAbstractTextAttributeNode) {
|
||||
return ((UiAbstractTextAttributeNode)element).getCurrentValue();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addListener(ILabelProviderListener listener) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public boolean isLabelProperty(Object element, String property) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
public void removeListener(ILabelProviderListener listener) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiListAttributeNode;
|
||||
|
||||
/**
|
||||
* Describes a text attribute that can only contain boolean values.
|
||||
* It is displayed by a {@link UiListAttributeNode}.
|
||||
*/
|
||||
public class BooleanAttributeDescriptor extends ListAttributeDescriptor {
|
||||
|
||||
public BooleanAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip,
|
||||
new String[] { "true", "false" } );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,751 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.resources.ResourceType;
|
||||
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo;
|
||||
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo.Format;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
/**
|
||||
* Utility methods related to descriptors handling.
|
||||
*/
|
||||
public final class DescriptorsUtils {
|
||||
|
||||
private static final int JAVADOC_BREAK_LENGTH = 60;
|
||||
|
||||
/**
|
||||
* The path in the online documentation for the manifest description.
|
||||
* <p/>
|
||||
* This is NOT a complete URL. To be used, it needs to be appended
|
||||
* to {@link AndroidConstants#CODESITE_BASE_URL} or to the local SDK
|
||||
* documentation.
|
||||
*/
|
||||
public static final String MANIFEST_SDK_URL = "/reference/android/R.styleable.html#"; //$NON-NLS-1$
|
||||
|
||||
public static final String IMAGE_KEY = "image"; //$NON-NLS-1$
|
||||
|
||||
private static final String CODE = "$code"; //$NON-NLS-1$
|
||||
private static final String LINK = "$link"; //$NON-NLS-1$
|
||||
private static final String ELEM = "$elem"; //$NON-NLS-1$
|
||||
private static final String BREAK = "$break"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* The {@link ITextAttributeCreator} interface is used by the appendAttribute() method
|
||||
* to provide a way for caller to override the kind of {@link TextAttributeDescriptor}
|
||||
* created for a give XML attribute name.
|
||||
*/
|
||||
public interface ITextAttributeCreator {
|
||||
/**
|
||||
* Creates a new {@link TextAttributeDescriptor} instance for the given XML name,
|
||||
* UI name and tooltip.
|
||||
*
|
||||
* @param xmlName The XML attribute name.
|
||||
* @param uiName The UI attribute name.
|
||||
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
|
||||
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
|
||||
* @param tooltip An optional tooltip.
|
||||
* @return A new {@link TextAttributeDescriptor} (or derived) instance.
|
||||
*/
|
||||
public TextAttributeDescriptor create(String xmlName, String uiName, String nsUri,
|
||||
String tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all {@link AttributeInfo} to the the array of {@link AttributeDescriptor}.
|
||||
*
|
||||
* @param attributes The list of {@link AttributeDescriptor} to append to
|
||||
* @param elementXmlName Optional XML local name of the element to which attributes are
|
||||
* being added. When not null, this is used to filter overrides.
|
||||
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
|
||||
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
|
||||
* @param infos The array of {@link AttributeInfo} to read and append to attributes
|
||||
* @param requiredAttributes An optional list of attributes to mark as "required" (i.e. append
|
||||
* a "*" to their UI name as a hint for the user.)
|
||||
* @param overrides A map [attribute name => TextAttributeDescriptor creator]. A creator
|
||||
* can either by a Class<? extends TextAttributeDescriptor> or an instance of
|
||||
* {@link ITextAttributeCreator} that instantiates the right TextAttributeDescriptor.
|
||||
*/
|
||||
public static void appendAttributes(ArrayList<AttributeDescriptor> attributes,
|
||||
String elementXmlName,
|
||||
String nsUri, AttributeInfo[] infos,
|
||||
String[] requiredAttributes,
|
||||
Map<String, Object> overrides) {
|
||||
for (AttributeInfo info : infos) {
|
||||
boolean required = false;
|
||||
if (requiredAttributes != null) {
|
||||
for(String attr_name : requiredAttributes) {
|
||||
if (attr_name.equals(info.getName())) {
|
||||
required = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
appendAttribute(attributes, elementXmlName, nsUri, info, required, overrides);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an {@link AttributeInfo} to the the array of {@link AttributeDescriptor}.
|
||||
*
|
||||
* @param attributes The list of {@link AttributeDescriptor} to append to
|
||||
* @param elementXmlName Optional XML local name of the element to which attributes are
|
||||
* being added. When not null, this is used to filter overrides.
|
||||
* @param info The {@link AttributeInfo} to append to attributes
|
||||
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
|
||||
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
|
||||
* @param required True if the attribute is to be marked as "required" (i.e. append
|
||||
* a "*" to its UI name as a hint for the user.)
|
||||
* @param overrides A map [attribute name => TextAttributeDescriptor creator]. A creator
|
||||
* can either by a Class<? extends TextAttributeDescriptor> or an instance of
|
||||
* {@link ITextAttributeCreator} that instantiates the right TextAttributeDescriptor.
|
||||
*/
|
||||
public static void appendAttribute(ArrayList<AttributeDescriptor> attributes,
|
||||
String elementXmlName,
|
||||
String nsUri,
|
||||
AttributeInfo info, boolean required,
|
||||
Map<String, Object> overrides) {
|
||||
AttributeDescriptor attr = null;
|
||||
|
||||
String xmlLocalName = info.getName();
|
||||
String uiName = prettyAttributeUiName(info.getName()); // ui_name
|
||||
if (required) {
|
||||
uiName += "*"; //$NON-NLS-1$
|
||||
}
|
||||
String tooltip = formatTooltip(info.getJavaDoc()); // tooltip
|
||||
|
||||
// Add the known types to the tooltip
|
||||
Format[] formats_list = info.getFormats();
|
||||
int flen = formats_list.length;
|
||||
if (flen > 0) {
|
||||
// Fill the formats in a set for faster access
|
||||
HashSet<Format> formats_set = new HashSet<Format>();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (tooltip != null) {
|
||||
sb.append(tooltip);
|
||||
sb.append(" "); //$NON-NLS-1$
|
||||
}
|
||||
sb.append("["); //$NON-NLS-1$
|
||||
for (int i = 0; i < flen; i++) {
|
||||
Format f = formats_list[i];
|
||||
formats_set.add(f);
|
||||
|
||||
sb.append(f.toString().toLowerCase());
|
||||
if (i < flen - 1) {
|
||||
sb.append(", "); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
// The extra space at the end makes the tooltip more readable on Windows.
|
||||
sb.append("]"); //$NON-NLS-1$
|
||||
|
||||
if (required) {
|
||||
sb.append(". Required.");
|
||||
}
|
||||
|
||||
// The extra space at the end makes the tooltip more readable on Windows.
|
||||
sb.append(" "); //$NON-NLS-1$
|
||||
|
||||
tooltip = sb.toString();
|
||||
|
||||
// Create a specialized attribute if we can
|
||||
if (overrides != null) {
|
||||
for (Entry<String, Object> entry: overrides.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String elements[] = key.split("/");
|
||||
String overrideAttrLocalName = null;
|
||||
if (elements.length < 1) {
|
||||
continue;
|
||||
} else if (elements.length == 1) {
|
||||
overrideAttrLocalName = elements[0];
|
||||
elements = null;
|
||||
} else {
|
||||
overrideAttrLocalName = elements[elements.length - 1];
|
||||
elements = elements[0].split(",");
|
||||
}
|
||||
|
||||
if (overrideAttrLocalName == null ||
|
||||
!overrideAttrLocalName.equals(xmlLocalName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean ok_element = elements.length < 1;
|
||||
if (!ok_element) {
|
||||
for (String element : elements) {
|
||||
if (element.equals("*") || element.equals(elementXmlName)) {
|
||||
ok_element = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok_element) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object override = entry.getValue();
|
||||
if (override instanceof Class) {
|
||||
try {
|
||||
// The override is instance of the class to create, which must
|
||||
// have a constructor compatible with TextAttributeDescriptor.
|
||||
@SuppressWarnings("unchecked") //$NON-NLS-1$
|
||||
Class<? extends TextAttributeDescriptor> clazz =
|
||||
(Class<? extends TextAttributeDescriptor>) override;
|
||||
Constructor<? extends TextAttributeDescriptor> cons;
|
||||
cons = clazz.getConstructor(new Class<?>[] {
|
||||
String.class, String.class, String.class, String.class } );
|
||||
attr = cons.newInstance(
|
||||
new Object[] { xmlLocalName, uiName, nsUri, tooltip });
|
||||
} catch (SecurityException e) {
|
||||
// ignore
|
||||
} catch (NoSuchMethodException e) {
|
||||
// ignore
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ignore
|
||||
} catch (InstantiationException e) {
|
||||
// ignore
|
||||
} catch (IllegalAccessException e) {
|
||||
// ignore
|
||||
} catch (InvocationTargetException e) {
|
||||
// ignore
|
||||
}
|
||||
} else if (override instanceof ITextAttributeCreator) {
|
||||
attr = ((ITextAttributeCreator) override).create(
|
||||
xmlLocalName, uiName, nsUri, tooltip);
|
||||
}
|
||||
}
|
||||
} // if overrides
|
||||
|
||||
// Create a specialized descriptor if we can, based on type
|
||||
if (attr == null) {
|
||||
if (formats_set.contains(Format.REFERENCE)) {
|
||||
// This is either a multi-type reference or a generic reference.
|
||||
attr = new ReferenceAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip);
|
||||
} else if (formats_set.contains(Format.ENUM)) {
|
||||
attr = new ListAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip,
|
||||
info.getEnumValues());
|
||||
} else if (formats_set.contains(Format.FLAG)) {
|
||||
attr = new FlagAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip,
|
||||
info.getFlagValues());
|
||||
} else if (formats_set.contains(Format.BOOLEAN)) {
|
||||
attr = new BooleanAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip);
|
||||
} else if (formats_set.contains(Format.STRING)) {
|
||||
attr = new ReferenceAttributeDescriptor(ResourceType.STRING,
|
||||
xmlLocalName, uiName, nsUri,
|
||||
tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// By default a simple text field is used
|
||||
if (attr == null) {
|
||||
attr = new TextAttributeDescriptor(xmlLocalName, uiName, nsUri, tooltip);
|
||||
}
|
||||
attributes.add(attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the the given {@link AttributeInfo} already exists in the ArrayList of
|
||||
* {@link AttributeDescriptor}. This test for the presence of a descriptor with the same
|
||||
* XML name.
|
||||
*
|
||||
* @param attributes The list of {@link AttributeDescriptor} to compare to.
|
||||
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
|
||||
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
|
||||
* @param info The {@link AttributeInfo} to know whether it is included in the above list.
|
||||
* @return True if this {@link AttributeInfo} is already present in
|
||||
* the {@link AttributeDescriptor} list.
|
||||
*/
|
||||
public static boolean containsAttribute(ArrayList<AttributeDescriptor> attributes,
|
||||
String nsUri,
|
||||
AttributeInfo info) {
|
||||
String xmlLocalName = info.getName();
|
||||
for (AttributeDescriptor desc : attributes) {
|
||||
if (desc.getXmlLocalName().equals(xmlLocalName)) {
|
||||
if (nsUri == desc.getNamespaceUri() ||
|
||||
(nsUri != null && nsUri.equals(desc.getNamespaceUri()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a pretty attribute UI name from an XML name.
|
||||
* <p/>
|
||||
* The original xml name starts with a lower case and is camel-case,
|
||||
* e.g. "maxWidthForView". The pretty name starts with an upper case
|
||||
* and has space separators, e.g. "Max width for view".
|
||||
*/
|
||||
public static String prettyAttributeUiName(String name) {
|
||||
if (name.length() < 1) {
|
||||
return name;
|
||||
}
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
char c = name.charAt(0);
|
||||
// Use upper case initial letter
|
||||
buf.append((char)(c >= 'a' && c <= 'z' ? c + 'A' - 'a' : c));
|
||||
int len = name.length();
|
||||
for (int i = 1; i < len; i++) {
|
||||
c = name.charAt(i);
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
// Break camel case into separate words
|
||||
buf.append(' ');
|
||||
// Use a lower case initial letter for the next word, except if the
|
||||
// word is solely X, Y or Z.
|
||||
if (c >= 'X' && c <= 'Z' &&
|
||||
(i == len-1 ||
|
||||
(i < len-1 && name.charAt(i+1) >= 'A' && name.charAt(i+1) <= 'Z'))) {
|
||||
buf.append(c);
|
||||
} else {
|
||||
buf.append((char)(c - 'A' + 'a'));
|
||||
}
|
||||
} else if (c == '_') {
|
||||
buf.append(' ');
|
||||
} else {
|
||||
buf.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
name = buf.toString();
|
||||
|
||||
// Replace these acronyms by upper-case versions
|
||||
// - (?<=^| ) means "if preceded by a space or beginning of string"
|
||||
// - (?=$| ) means "if followed by a space or end of string"
|
||||
name = name.replaceAll("(?<=^| )sdk(?=$| )", "SDK");
|
||||
name = name.replaceAll("(?<=^| )uri(?=$| )", "URI");
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalizes the string, i.e. transforms the initial [a-z] into [A-Z].
|
||||
* Returns the string unmodified if the first character is not [a-z].
|
||||
*
|
||||
* @param str The string to capitalize.
|
||||
* @return The capitalized string
|
||||
*/
|
||||
public static String capitalize(String str) {
|
||||
if (str == null || str.length() < 1 || str.charAt(0) < 'a' || str.charAt(0) > 'z') {
|
||||
return str;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append((char)(str.charAt(0) + 'A' - 'a'));
|
||||
sb.append(str.substring(1));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the javadoc tooltip to be usable in a tooltip.
|
||||
*/
|
||||
public static String formatTooltip(String javadoc) {
|
||||
ArrayList<String> spans = scanJavadoc(javadoc);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean needBreak = false;
|
||||
|
||||
for (int n = spans.size(), i = 0; i < n; ++i) {
|
||||
String s = spans.get(i);
|
||||
if (CODE.equals(s)) {
|
||||
s = spans.get(++i);
|
||||
if (s != null) {
|
||||
sb.append('"').append(s).append('"');
|
||||
}
|
||||
} else if (LINK.equals(s)) {
|
||||
String base = spans.get(++i);
|
||||
String anchor = spans.get(++i);
|
||||
String text = spans.get(++i);
|
||||
|
||||
if (base != null) {
|
||||
base = base.trim();
|
||||
}
|
||||
if (anchor != null) {
|
||||
anchor = anchor.trim();
|
||||
}
|
||||
if (text != null) {
|
||||
text = text.trim();
|
||||
}
|
||||
|
||||
// If there's no text, use the anchor if there's one
|
||||
if (text == null || text.length() == 0) {
|
||||
text = anchor;
|
||||
}
|
||||
|
||||
if (base != null && base.length() > 0) {
|
||||
if (text == null || text.length() == 0) {
|
||||
// If we still have no text, use the base as text
|
||||
text = base;
|
||||
}
|
||||
}
|
||||
|
||||
if (text != null) {
|
||||
sb.append(text);
|
||||
}
|
||||
|
||||
} else if (ELEM.equals(s)) {
|
||||
s = spans.get(++i);
|
||||
if (s != null) {
|
||||
sb.append(s);
|
||||
}
|
||||
} else if (BREAK.equals(s)) {
|
||||
needBreak = true;
|
||||
} else if (s != null) {
|
||||
if (needBreak && s.trim().length() > 0) {
|
||||
sb.append('\r');
|
||||
}
|
||||
sb.append(s);
|
||||
needBreak = false;
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the javadoc tooltip to be usable in a FormText.
|
||||
* <p/>
|
||||
* If the descriptor can provide an icon, the caller should provide
|
||||
* elementsDescriptor.getIcon() as "image" to FormText, e.g.:
|
||||
* <code>formText.setImage(IMAGE_KEY, elementsDescriptor.getIcon());</code>
|
||||
*
|
||||
* @param javadoc The javadoc to format. Cannot be null.
|
||||
* @param elementDescriptor The element descriptor parent of the javadoc. Cannot be null.
|
||||
* @param androidDocBaseUrl The base URL for the documentation. Cannot be null. Should be
|
||||
* <code>FrameworkResourceManager.getInstance().getDocumentationBaseUrl()</code>
|
||||
*/
|
||||
public static String formatFormText(String javadoc,
|
||||
ElementDescriptor elementDescriptor,
|
||||
String androidDocBaseUrl) {
|
||||
ArrayList<String> spans = scanJavadoc(javadoc);
|
||||
|
||||
String fullSdkUrl = androidDocBaseUrl + MANIFEST_SDK_URL;
|
||||
String sdkUrl = elementDescriptor.getSdkUrl();
|
||||
if (sdkUrl != null && sdkUrl.startsWith(MANIFEST_SDK_URL)) {
|
||||
fullSdkUrl = androidDocBaseUrl + sdkUrl;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
Image icon = elementDescriptor.getIcon();
|
||||
if (icon != null) {
|
||||
sb.append("<form><li style=\"image\" value=\"" + IMAGE_KEY + "\">"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
} else {
|
||||
sb.append("<form><p>"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
for (int n = spans.size(), i = 0; i < n; ++i) {
|
||||
String s = spans.get(i);
|
||||
if (CODE.equals(s)) {
|
||||
s = spans.get(++i);
|
||||
if (elementDescriptor.getXmlName().equals(s) && fullSdkUrl != null) {
|
||||
sb.append("<a href=\""); //$NON-NLS-1$
|
||||
sb.append(fullSdkUrl);
|
||||
sb.append("\">"); //$NON-NLS-1$
|
||||
sb.append(s);
|
||||
sb.append("</a>"); //$NON-NLS-1$
|
||||
} else if (s != null) {
|
||||
sb.append('"').append(s).append('"');
|
||||
}
|
||||
} else if (LINK.equals(s)) {
|
||||
String base = spans.get(++i);
|
||||
String anchor = spans.get(++i);
|
||||
String text = spans.get(++i);
|
||||
|
||||
if (base != null) {
|
||||
base = base.trim();
|
||||
}
|
||||
if (anchor != null) {
|
||||
anchor = anchor.trim();
|
||||
}
|
||||
if (text != null) {
|
||||
text = text.trim();
|
||||
}
|
||||
|
||||
// If there's no text, use the anchor if there's one
|
||||
if (text == null || text.length() == 0) {
|
||||
text = anchor;
|
||||
}
|
||||
|
||||
// TODO specialize with a base URL for views, menus & other resources
|
||||
// Base is empty for a local page anchor, in which case we'll replace it
|
||||
// by the element SDK URL if it exists.
|
||||
if ((base == null || base.length() == 0) && fullSdkUrl != null) {
|
||||
base = fullSdkUrl;
|
||||
}
|
||||
|
||||
String url = null;
|
||||
if (base != null && base.length() > 0) {
|
||||
if (base.startsWith("http")) { //$NON-NLS-1$
|
||||
// If base looks an URL, use it, with the optional anchor
|
||||
url = base;
|
||||
if (anchor != null && anchor.length() > 0) {
|
||||
// If the base URL already has an anchor, it needs to be
|
||||
// removed first. If there's no anchor, we need to add "#"
|
||||
int pos = url.lastIndexOf('#');
|
||||
if (pos < 0) {
|
||||
url += "#"; //$NON-NLS-1$
|
||||
} else if (pos < url.length() - 1) {
|
||||
url = url.substring(0, pos + 1);
|
||||
}
|
||||
|
||||
url += anchor;
|
||||
}
|
||||
} else if (text == null || text.length() == 0) {
|
||||
// If we still have no text, use the base as text
|
||||
text = base;
|
||||
}
|
||||
}
|
||||
|
||||
if (url != null && text != null) {
|
||||
sb.append("<a href=\""); //$NON-NLS-1$
|
||||
sb.append(url);
|
||||
sb.append("\">"); //$NON-NLS-1$
|
||||
sb.append(text);
|
||||
sb.append("</a>"); //$NON-NLS-1$
|
||||
} else if (text != null) {
|
||||
sb.append("<b>").append(text).append("</b>"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
} else if (ELEM.equals(s)) {
|
||||
s = spans.get(++i);
|
||||
if (sdkUrl != null && s != null) {
|
||||
sb.append("<a href=\""); //$NON-NLS-1$
|
||||
sb.append(sdkUrl);
|
||||
sb.append("\">"); //$NON-NLS-1$
|
||||
sb.append(s);
|
||||
sb.append("</a>"); //$NON-NLS-1$
|
||||
} else if (s != null) {
|
||||
sb.append("<b>").append(s).append("</b>"); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
} else if (BREAK.equals(s)) {
|
||||
// ignore line breaks in pseudo-HTML rendering
|
||||
} else if (s != null) {
|
||||
sb.append(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (icon != null) {
|
||||
sb.append("</li></form>"); //$NON-NLS-1$
|
||||
} else {
|
||||
sb.append("</p></form>"); //$NON-NLS-1$
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static ArrayList<String> scanJavadoc(String javadoc) {
|
||||
ArrayList<String> spans = new ArrayList<String>();
|
||||
|
||||
// Standardize all whitespace in the javadoc to single spaces.
|
||||
if (javadoc != null) {
|
||||
javadoc = javadoc.replaceAll("[ \t\f\r\n]+", " "); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
// Detects {@link <base>#<name> <text>} where all 3 are optional
|
||||
Pattern p_link = Pattern.compile("\\{@link\\s+([^#\\}\\s]*)(?:#([^\\s\\}]*))?(?:\\s*([^\\}]*))?\\}(.*)"); //$NON-NLS-1$
|
||||
// Detects <code>blah</code>
|
||||
Pattern p_code = Pattern.compile("<code>(.+?)</code>(.*)"); //$NON-NLS-1$
|
||||
// Detects @blah@, used in hard-coded tooltip descriptors
|
||||
Pattern p_elem = Pattern.compile("@([\\w -]+)@(.*)"); //$NON-NLS-1$
|
||||
// Detects a buffer that starts by @ < or { (one that was not matched above)
|
||||
Pattern p_open = Pattern.compile("([@<\\{])(.*)"); //$NON-NLS-1$
|
||||
// Detects everything till the next potential separator, i.e. @ < or {
|
||||
Pattern p_text = Pattern.compile("([^@<\\{]+)(.*)"); //$NON-NLS-1$
|
||||
|
||||
int currentLength = 0;
|
||||
String text = null;
|
||||
|
||||
while(javadoc != null && javadoc.length() > 0) {
|
||||
Matcher m;
|
||||
String s = null;
|
||||
if ((m = p_code.matcher(javadoc)).matches()) {
|
||||
spans.add(CODE);
|
||||
spans.add(text = cleanupJavadocHtml(m.group(1))); // <code> text
|
||||
javadoc = m.group(2);
|
||||
if (text != null) {
|
||||
currentLength += text.length();
|
||||
}
|
||||
} else if ((m = p_link.matcher(javadoc)).matches()) {
|
||||
spans.add(LINK);
|
||||
spans.add(m.group(1)); // @link base
|
||||
spans.add(m.group(2)); // @link anchor
|
||||
spans.add(text = cleanupJavadocHtml(m.group(3))); // @link text
|
||||
javadoc = m.group(4);
|
||||
if (text != null) {
|
||||
currentLength += text.length();
|
||||
}
|
||||
} else if ((m = p_elem.matcher(javadoc)).matches()) {
|
||||
spans.add(ELEM);
|
||||
spans.add(text = cleanupJavadocHtml(m.group(1))); // @text@
|
||||
javadoc = m.group(2);
|
||||
if (text != null) {
|
||||
currentLength += text.length() - 2;
|
||||
}
|
||||
} else if ((m = p_open.matcher(javadoc)).matches()) {
|
||||
s = m.group(1);
|
||||
javadoc = m.group(2);
|
||||
} else if ((m = p_text.matcher(javadoc)).matches()) {
|
||||
s = m.group(1);
|
||||
javadoc = m.group(2);
|
||||
} else {
|
||||
// This is not supposed to happen. In case of, just use everything.
|
||||
s = javadoc;
|
||||
javadoc = null;
|
||||
}
|
||||
if (s != null && s.length() > 0) {
|
||||
s = cleanupJavadocHtml(s);
|
||||
|
||||
if (currentLength >= JAVADOC_BREAK_LENGTH) {
|
||||
spans.add(BREAK);
|
||||
currentLength = 0;
|
||||
}
|
||||
while (currentLength + s.length() > JAVADOC_BREAK_LENGTH) {
|
||||
int pos = s.indexOf(' ', JAVADOC_BREAK_LENGTH - currentLength);
|
||||
if (pos <= 0) {
|
||||
break;
|
||||
}
|
||||
spans.add(s.substring(0, pos + 1));
|
||||
spans.add(BREAK);
|
||||
currentLength = 0;
|
||||
s = s.substring(pos + 1);
|
||||
}
|
||||
|
||||
spans.add(s);
|
||||
currentLength += s.length();
|
||||
}
|
||||
}
|
||||
|
||||
return spans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove anything that looks like HTML from a javadoc snippet, as it is supported
|
||||
* neither by FormText nor a standard text tooltip.
|
||||
*/
|
||||
private static String cleanupJavadocHtml(String s) {
|
||||
if (s != null) {
|
||||
s = s.replaceAll("<", "\""); //$NON-NLS-1$ $NON-NLS-2$
|
||||
s = s.replaceAll(">", "\""); //$NON-NLS-1$ $NON-NLS-2$
|
||||
s = s.replaceAll("<[^>]+>", ""); //$NON-NLS-1$ $NON-NLS-2$
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default layout attributes for the a new UiElementNode.
|
||||
* <p/>
|
||||
* Note that ideally the node should already be part of a hierarchy so that its
|
||||
* parent layout and previous sibling can be determined, if any.
|
||||
* <p/>
|
||||
* This does not override attributes which are not empty.
|
||||
*/
|
||||
public static void setDefaultLayoutAttributes(UiElementNode ui_node) {
|
||||
ui_node.setAttributeValue("layout_width", "wrap_content", false /* override */); //$NON-NLS-1$ $NON-NLS-2$
|
||||
ui_node.setAttributeValue("layout_height", "wrap_content", false /* override */); //$NON-NLS-1$ $NON-NLS-2$
|
||||
|
||||
String widget_id = getFreeWidgetId(ui_node.getUiRoot(),
|
||||
new Object[] {ui_node.getDescriptor().getXmlLocalName(), 1, null });
|
||||
if (widget_id != null) {
|
||||
ui_node.setAttributeValue("id", "@+id/" + widget_id, false /* override */); //$NON-NLS-1$ $NON-NLS-2$
|
||||
}
|
||||
|
||||
UiElementNode ui_parent = ui_node.getUiParent();
|
||||
if (ui_parent != null && ui_parent.getDescriptor().getXmlLocalName().equals("RelativeLayout")) { //$NON-NLS-1$
|
||||
UiElementNode ui_previous = ui_node.getUiPreviousSibling();
|
||||
if (ui_previous != null) {
|
||||
String id = ui_previous.getAttributeValue("id"); //$NON-NLS-1$
|
||||
if (id != null && id.length() > 0) {
|
||||
id = id.replace("@+", "@");
|
||||
ui_node.setAttributeValue("layout_below", id, false /* override */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a UI root node, returns the first available id that matches the
|
||||
* pattern "prefix%02d".
|
||||
*
|
||||
* For recursion purposes, a "context" is given. Since Java doesn't have in-out parameters
|
||||
* in methods and we're not going to do a dedicated type, we just use an object array which
|
||||
* must contain the following items:
|
||||
* - prefix(String): The prefix of the generated id, i.e. "widget"
|
||||
* - index(Integer): The minimum index of the generated id
|
||||
* - generated(String) The generated widget currently being searched. Must start with null.
|
||||
*
|
||||
* @param uiRoot The Ui root node where to start searching recusrively.
|
||||
* @param params An in-out context of parameters used during recursion, as explaine above.
|
||||
* @return A suitable generated id
|
||||
*/
|
||||
private static String getFreeWidgetId(UiElementNode uiRoot, Object[] params) {
|
||||
String generated = (String) params[2];
|
||||
if (generated == null) {
|
||||
String prefix = (String) params[0];
|
||||
int pos = prefix.indexOf('.');
|
||||
if (pos >= 0) {
|
||||
prefix = prefix.substring(pos + 1);
|
||||
}
|
||||
pos = prefix.indexOf('$');
|
||||
if (pos >= 0) {
|
||||
prefix = prefix.substring(pos + 1);
|
||||
}
|
||||
prefix = prefix.replaceAll("[^a-zA-Z]", ""); //$NON-NLS-1$ $NON-NLS-2$
|
||||
if (prefix.length() == 0) {
|
||||
prefix = "widget";
|
||||
}
|
||||
generated = String.format("%1$s%2$02d", prefix, ((Integer)params[1]).intValue());
|
||||
params[0] = prefix;
|
||||
params[2] = generated;
|
||||
}
|
||||
|
||||
String id = uiRoot.getAttributeValue("id"); //$NON-NLS-1$
|
||||
if (id != null) {
|
||||
id = id.replace("@+id/", ""); //$NON-NLS-1$ $NON-NLS-2$
|
||||
id = id.replace("@id/", ""); //$NON-NLS-1$ $NON-NLS-2$
|
||||
if (id.equals(generated)) {
|
||||
// switch to next value
|
||||
int num = ((Integer)params[1]).intValue() + 1;
|
||||
generated = String.format("%1$s%2$02d", params[0], num);
|
||||
params[1] = num;
|
||||
params[2] = generated;
|
||||
}
|
||||
}
|
||||
|
||||
for (UiElementNode uiChild : uiRoot.getUiChildren()) {
|
||||
getFreeWidgetId(uiChild, params);
|
||||
}
|
||||
|
||||
return (String) params[2];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
/**
|
||||
* {@link DocumentDescriptor} describes the properties expected for an XML document node.
|
||||
*
|
||||
* Compared to ElementDescriptor, {@link DocumentDescriptor} does not have XML name nor UI name,
|
||||
* tooltip, SDK url and attributes list.
|
||||
* <p/>
|
||||
* It has a children list which represent all the possible roots of the document.
|
||||
* <p/>
|
||||
* The document nodes are "mandatory", meaning the UI node is never deleted and it may lack
|
||||
* an actual XML node attached.
|
||||
*/
|
||||
public class DocumentDescriptor extends ElementDescriptor {
|
||||
|
||||
/**
|
||||
* Constructs a new {@link DocumentDescriptor} based on its XML name and children list.
|
||||
* The UI name is build by capitalizing the XML name.
|
||||
* The UI nodes will be non-mandatory.
|
||||
* <p/>
|
||||
* The XML name is never shown in the UI directly. It is however used when an icon
|
||||
* needs to be found for the node.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
* @param children The list of allowed children. Can be null or empty.
|
||||
*/
|
||||
public DocumentDescriptor(String xml_name, ElementDescriptor[] children) {
|
||||
super(xml_name, children, true /* mandatory */);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiElementNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiElementNode createUiNode() {
|
||||
return new UiDocumentNode(this);
|
||||
}
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.IconFactory;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.jface.resource.ImageDescriptor;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* {@link ElementDescriptor} describes the properties expected for a given XML element node.
|
||||
*
|
||||
* {@link ElementDescriptor} have an XML name, UI name, a tooltip, an SDK url,
|
||||
* an attributes list and a children list.
|
||||
*
|
||||
* An UI node can be "mandatory", meaning the UI node is never deleted and it may lack
|
||||
* an actual XML node attached. A non-mandatory UI node MUST have an XML node attached
|
||||
* and it will cease to exist when the XML node ceases to exist.
|
||||
*/
|
||||
public class ElementDescriptor {
|
||||
/** The XML element node name. Case sensitive. */
|
||||
private String mXmlName;
|
||||
/** The XML element name for the user interface, typically capitalized. */
|
||||
private String mUiName;
|
||||
/** The list of allowed attributes. */
|
||||
private AttributeDescriptor[] mAttributes;
|
||||
/** The list of allowed children */
|
||||
private ElementDescriptor[] mChildren;
|
||||
/* An optional tooltip. Can be empty. */
|
||||
private String mTooltip;
|
||||
/** An optional SKD URL. Can be empty. */
|
||||
private String mSdkUrl;
|
||||
/** Whether this UI node must always exist (even for empty models). */
|
||||
private boolean mMandatory;
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ElementDescriptor} based on its XML name, UI name,
|
||||
* tooltip, SDK url, attributes list, children list and mandatory.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
* @param ui_name The XML element name for the user interface, typically capitalized.
|
||||
* @param tooltip An optional tooltip. Can be null or empty.
|
||||
* @param sdk_url An optional SKD URL. Can be null or empty.
|
||||
* @param attributes The list of allowed attributes. Can be null or empty.
|
||||
* @param children The list of allowed children. Can be null or empty.
|
||||
* @param mandatory Whether this node must always exist (even for empty models). A mandatory
|
||||
* UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
|
||||
* UI node MUST have an XML node attached and it will cease to exist when the XML node
|
||||
* ceases to exist.
|
||||
*/
|
||||
public ElementDescriptor(String xml_name, String ui_name, String tooltip, String sdk_url,
|
||||
AttributeDescriptor[] attributes,
|
||||
ElementDescriptor[] children,
|
||||
boolean mandatory) {
|
||||
mMandatory = mandatory;
|
||||
mXmlName = xml_name;
|
||||
mUiName = ui_name;
|
||||
mTooltip = (tooltip != null && tooltip.length() > 0) ? tooltip : null;
|
||||
mSdkUrl = (sdk_url != null && sdk_url.length() > 0) ? sdk_url : null;
|
||||
setAttributes(attributes != null ? attributes : new AttributeDescriptor[]{});
|
||||
mChildren = children != null ? children : new ElementDescriptor[]{};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ElementDescriptor} based on its XML name and children list.
|
||||
* The UI name is build by capitalizing the XML name.
|
||||
* The UI nodes will be non-mandatory.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
* @param children The list of allowed children. Can be null or empty.
|
||||
* @param mandatory Whether this node must always exist (even for empty models). A mandatory
|
||||
* UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
|
||||
* UI node MUST have an XML node attached and it will cease to exist when the XML node
|
||||
* ceases to exist.
|
||||
*/
|
||||
public ElementDescriptor(String xml_name, ElementDescriptor[] children, boolean mandatory) {
|
||||
this(xml_name, prettyName(xml_name), null, null, null, children, mandatory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ElementDescriptor} based on its XML name and children list.
|
||||
* The UI name is build by capitalizing the XML name.
|
||||
* The UI nodes will be non-mandatory.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
* @param children The list of allowed children. Can be null or empty.
|
||||
*/
|
||||
public ElementDescriptor(String xml_name, ElementDescriptor[] children) {
|
||||
this(xml_name, prettyName(xml_name), null, null, null, children, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ElementDescriptor} based on its XML name.
|
||||
* The UI name is build by capitalizing the XML name.
|
||||
* The UI nodes will be non-mandatory.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
*/
|
||||
public ElementDescriptor(String xml_name) {
|
||||
this(xml_name, prettyName(xml_name), null, null, null, null, false);
|
||||
}
|
||||
|
||||
/** Returns whether this node must always exist (even for empty models) */
|
||||
public boolean isMandatory() {
|
||||
return mMandatory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the XML element node local name (case sensitive)
|
||||
*/
|
||||
public final String getXmlLocalName() {
|
||||
int pos = mXmlName.indexOf(':');
|
||||
if (pos != -1) {
|
||||
return mXmlName.substring(pos+1);
|
||||
}
|
||||
return mXmlName;
|
||||
}
|
||||
|
||||
/** Returns the XML element node name. Case sensitive. */
|
||||
public String getXmlName() {
|
||||
return mXmlName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace of the attribute.
|
||||
*/
|
||||
public final String getNamespace() {
|
||||
// For now we hard-code the prefix as being "android"
|
||||
if (mXmlName.startsWith("android:")) { //$NON-NLs-1$
|
||||
return AndroidConstants.NS_RESOURCES;
|
||||
}
|
||||
|
||||
return ""; //$NON-NLs-1$
|
||||
}
|
||||
|
||||
|
||||
/** Returns the XML element name for the user interface, typically capitalized. */
|
||||
public String getUiName() {
|
||||
return mUiName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an optional icon for the element.
|
||||
* <p/>
|
||||
* By default this tries to return an icon based on the XML name of the element.
|
||||
* If this fails, it tries to return the default Android logo as defined in the
|
||||
* plugin. If all fails, it returns null.
|
||||
*
|
||||
* @return An icon for this element or null.
|
||||
*/
|
||||
public Image getIcon() {
|
||||
IconFactory factory = IconFactory.getInstance();
|
||||
int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN;
|
||||
int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE;
|
||||
Image icon = factory.getIcon(mXmlName, color, shape);
|
||||
return icon != null ? icon : EditorsPlugin.getAndroidLogo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an optional ImageDescriptor for the element.
|
||||
* <p/>
|
||||
* By default this tries to return an image based on the XML name of the element.
|
||||
* If this fails, it tries to return the default Android logo as defined in the
|
||||
* plugin. If all fails, it returns null.
|
||||
*
|
||||
* @return An ImageDescriptor for this element or null.
|
||||
*/
|
||||
public ImageDescriptor getImageDescriptor() {
|
||||
IconFactory factory = IconFactory.getInstance();
|
||||
int color = hasChildren() ? IconFactory.COLOR_BLUE : IconFactory.COLOR_GREEN;
|
||||
int shape = hasChildren() ? IconFactory.SHAPE_RECT : IconFactory.SHAPE_CIRCLE;
|
||||
ImageDescriptor id = factory.getImageDescriptor(mXmlName, color, shape);
|
||||
return id != null ? id : EditorsPlugin.getAndroidLogoDesc();
|
||||
}
|
||||
|
||||
/* Returns the list of allowed attributes. */
|
||||
public AttributeDescriptor[] getAttributes() {
|
||||
return mAttributes;
|
||||
}
|
||||
|
||||
/* Sets the list of allowed attributes. */
|
||||
public void setAttributes(AttributeDescriptor[] attributes) {
|
||||
mAttributes = attributes;
|
||||
for (AttributeDescriptor attribute : attributes) {
|
||||
attribute.setParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the list of allowed children */
|
||||
public ElementDescriptor[] getChildren() {
|
||||
return mChildren;
|
||||
}
|
||||
|
||||
/** @return True if this descriptor has children available */
|
||||
public boolean hasChildren() {
|
||||
return mChildren.length > 0;
|
||||
}
|
||||
|
||||
/** Sets the list of allowed children. */
|
||||
public void setChildren(ElementDescriptor[] newChildren) {
|
||||
mChildren = newChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an optional tooltip. Will be null if not present.
|
||||
* <p/>
|
||||
* The tooltip is based on the Javadoc of the element and already processed via
|
||||
* {@link DescriptorsUtils#formatTooltip(String)} to be displayed right away as
|
||||
* a UI tooltip.
|
||||
*/
|
||||
public String getTooltip() {
|
||||
return mTooltip;
|
||||
}
|
||||
|
||||
/** Returns an optional SKD URL. Will be null if not present. */
|
||||
public String getSdkUrl() {
|
||||
return mSdkUrl;
|
||||
}
|
||||
|
||||
/** Sets the optional tooltip. Can be null or empty. */
|
||||
public void setTooltip(String tooltip) {
|
||||
mTooltip = tooltip;
|
||||
}
|
||||
|
||||
/** Sets the optional SDK URL. Can be null or empty. */
|
||||
public void setSdkUrl(String sdkUrl) {
|
||||
mSdkUrl = sdkUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiElementNode} linked to this descriptor.
|
||||
*/
|
||||
public UiElementNode createUiNode() {
|
||||
return new UiElementNode(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first children of this descriptor that describes the given XML element name.
|
||||
* <p/>
|
||||
* In recursive mode, searches the direct children first before descending in the hierarchy.
|
||||
*
|
||||
* @return The ElementDescriptor matching the requested XML node element name or null.
|
||||
*/
|
||||
public ElementDescriptor findChildrenDescriptor(String element_name, boolean recursive) {
|
||||
return findChildrenDescriptorInternal(element_name, recursive, null);
|
||||
}
|
||||
|
||||
private ElementDescriptor findChildrenDescriptorInternal(String element_name,
|
||||
boolean recursive,
|
||||
Set<ElementDescriptor> visited) {
|
||||
if (recursive && visited == null) {
|
||||
visited = new HashSet<ElementDescriptor>();
|
||||
}
|
||||
|
||||
for (ElementDescriptor e : getChildren()) {
|
||||
if (e.getXmlName().equals(element_name)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
if (visited != null) {
|
||||
visited.add(this);
|
||||
}
|
||||
|
||||
if (recursive) {
|
||||
for (ElementDescriptor e : getChildren()) {
|
||||
if (visited != null) {
|
||||
if (!visited.add(e)) { // Set.add() returns false if element is already present
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ElementDescriptor f = e.findChildrenDescriptorInternal(element_name,
|
||||
recursive, visited);
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility helper than pretty-formats an XML Name for the UI.
|
||||
* This is used by the simplified constructor that takes only an XML element name.
|
||||
*
|
||||
* @param xml_name The XML name to convert.
|
||||
* @return The XML name with dashes replaced by spaces and capitalized.
|
||||
*/
|
||||
private static String prettyName(String xml_name) {
|
||||
char c[] = xml_name.toCharArray();
|
||||
if (c.length > 0) {
|
||||
c[0] = Character.toUpperCase(c[0]);
|
||||
}
|
||||
return new String(c).replace("-", " "); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiListAttributeNode;
|
||||
|
||||
/**
|
||||
* Describes a text attribute that can only contains some predefined values.
|
||||
* It is displayed by a {@link UiListAttributeNode}.
|
||||
*/
|
||||
public class EnumAttributeDescriptor extends ListAttributeDescriptor {
|
||||
|
||||
public EnumAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiListAttributeNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiListAttributeNode(this, uiParent);
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.ui.FlagValueCellEditor;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiFlagAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiListAttributeNode;
|
||||
|
||||
import org.eclipse.jface.viewers.CellEditor;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
|
||||
/**
|
||||
* Describes a text attribute that can only contains some predefined values.
|
||||
* It is displayed by a {@link UiListAttributeNode}.
|
||||
*
|
||||
* Note: in Android resources, a "flag" is a list of fixed values where one or
|
||||
* more values can be selected using an "or", e.g. "align='left|top'".
|
||||
* By contrast, an "enum" is a list of fixed values of which only one can be
|
||||
* selected at a given time, e.g. "gravity='right'".
|
||||
* <p/>
|
||||
* This class handles the "flag" case.
|
||||
* The "enum" case is done using {@link ListAttributeDescriptor}.
|
||||
*/
|
||||
public class FlagAttributeDescriptor extends TextAttributeDescriptor {
|
||||
|
||||
private String[] mNames;
|
||||
|
||||
/**
|
||||
* Creates a new {@link FlagAttributeDescriptor} which automatically gets its
|
||||
* values from the FrameworkResourceManager.
|
||||
*/
|
||||
public FlagAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link FlagAttributeDescriptor} which uses the provided values.
|
||||
*/
|
||||
public FlagAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip, String[] names) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
mNames = names;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The initial names of the flags. Can be null, in which case the Framework
|
||||
* resource parser should be checked.
|
||||
*/
|
||||
public String[] getNames() {
|
||||
return mNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiListAttributeNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiFlagAttributeNode(this, uiParent);
|
||||
}
|
||||
|
||||
// ------- IPropertyDescriptor Methods
|
||||
|
||||
@Override
|
||||
public CellEditor createPropertyEditor(Composite parent) {
|
||||
return new FlagValueCellEditor(parent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.ui.ListValueCellEditor;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiListAttributeNode;
|
||||
|
||||
import org.eclipse.jface.viewers.CellEditor;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
|
||||
/**
|
||||
* Describes a text attribute that can contains some predefined values.
|
||||
* It is displayed by a {@link UiListAttributeNode}.
|
||||
*/
|
||||
public class ListAttributeDescriptor extends TextAttributeDescriptor {
|
||||
|
||||
private String[] mValues = null;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ListAttributeDescriptor} which automatically gets its
|
||||
* values from the FrameworkResourceManager.
|
||||
*/
|
||||
public ListAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ListAttributeDescriptor} which uses the provided values.
|
||||
*/
|
||||
public ListAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip, String[] values) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
mValues = values;
|
||||
}
|
||||
|
||||
public String[] getValues() {
|
||||
return mValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiListAttributeNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiListAttributeNode(this, uiParent);
|
||||
}
|
||||
|
||||
// ------- IPropertyDescriptor Methods
|
||||
|
||||
@Override
|
||||
public CellEditor createPropertyEditor(Composite parent) {
|
||||
return new ListValueCellEditor(parent);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.resources.ResourceType;
|
||||
import com.android.ide.eclipse.editors.ui.ResourceValueCellEditor;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiResourceAttributeNode;
|
||||
|
||||
import org.eclipse.jface.viewers.CellEditor;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
|
||||
/**
|
||||
* Describes an XML attribute displayed containing a value or a reference to a resource.
|
||||
* It is displayed by a {@link UiResourceAttributeNode}.
|
||||
*/
|
||||
public final class ReferenceAttributeDescriptor extends TextAttributeDescriptor {
|
||||
|
||||
private ResourceType mResourceType;
|
||||
|
||||
/**
|
||||
* Creates a reference attributes that can contain any type of resources.
|
||||
* @param xmlLocalName The XML name of the attribute (case sensitive)
|
||||
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
|
||||
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
|
||||
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
|
||||
* @param tooltip A non-empty tooltip string or null
|
||||
*/
|
||||
public ReferenceAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reference attributes that can contain a reference to a specific
|
||||
* {@link ResourceType}.
|
||||
* @param resourceType The specific {@link ResourceType} that this reference attribute supports.
|
||||
* It can be <code>null</code>, in which case, all resource types are supported.
|
||||
* @param xmlLocalName The XML name of the attribute (case sensitive)
|
||||
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
|
||||
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
|
||||
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
|
||||
* @param tooltip A non-empty tooltip string or null
|
||||
*/
|
||||
public ReferenceAttributeDescriptor(ResourceType resourceType,
|
||||
String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
mResourceType = resourceType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return A new {@link UiResourceAttributeNode} linked to this reference descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiResourceAttributeNode(mResourceType, this, uiParent);
|
||||
}
|
||||
|
||||
// ------- IPropertyDescriptor Methods
|
||||
|
||||
@Override
|
||||
public CellEditor createPropertyEditor(Composite parent) {
|
||||
return new ResourceValueCellEditor(parent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiSeparatorAttributeNode;
|
||||
|
||||
/**
|
||||
* {@link SeparatorAttributeDescriptor} does not represent any real attribute.
|
||||
* <p/>
|
||||
* It is used to separate groups of attributes visually.
|
||||
*/
|
||||
public class SeparatorAttributeDescriptor extends AttributeDescriptor {
|
||||
|
||||
/**
|
||||
* Creates a new {@link SeparatorAttributeDescriptor}
|
||||
*/
|
||||
public SeparatorAttributeDescriptor(String label) {
|
||||
super(label /* xmlLocalName */, null /* nsUri */);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiAttributeNode} linked to this descriptor or null if this
|
||||
* attribute has no user interface.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiSeparatorAttributeNode(this, uiParent);
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.ui.TextValueCellEditor;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiTextAttributeNode;
|
||||
|
||||
import org.eclipse.jface.viewers.CellEditor;
|
||||
import org.eclipse.jface.viewers.ILabelProvider;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.ui.views.properties.IPropertyDescriptor;
|
||||
|
||||
|
||||
/**
|
||||
* Describes a textual XML attribute.
|
||||
* <p/>
|
||||
* Such an attribute has a tooltip and would typically be displayed by
|
||||
* {@link UiTextAttributeNode} using a label widget and text field.
|
||||
* <p/>
|
||||
* This is the "default" kind of attribute. If in doubt, use this.
|
||||
*/
|
||||
public class TextAttributeDescriptor extends AttributeDescriptor implements IPropertyDescriptor {
|
||||
private String mUiName;
|
||||
private String mTooltip;
|
||||
|
||||
/**
|
||||
* Creates a new {@link TextAttributeDescriptor}
|
||||
*
|
||||
* @param xmlLocalName The XML name of the attribute (case sensitive)
|
||||
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
|
||||
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
|
||||
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
|
||||
* @param tooltip A non-empty tooltip string or null
|
||||
*/
|
||||
public TextAttributeDescriptor(String xmlLocalName, String uiName,
|
||||
String nsUri, String tooltip) {
|
||||
super(xmlLocalName, nsUri);
|
||||
mUiName = uiName;
|
||||
mTooltip = (tooltip != null && tooltip.length() > 0) ? tooltip : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The UI name of the attribute. Cannot be an empty string and cannot be null.
|
||||
*/
|
||||
public final String getUiName() {
|
||||
return mUiName;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tooltip string is either null or a non-empty string.
|
||||
* <p/>
|
||||
* The tooltip is based on the Javadoc of the attribute and already processed via
|
||||
* {@link DescriptorsUtils#formatTooltip(String)} to be displayed right away as
|
||||
* a UI tooltip.
|
||||
* <p/>
|
||||
* An empty string is converted to null, to match the behavior of setToolTipText() in
|
||||
* {@link Control}.
|
||||
*
|
||||
* @return A non-empty tooltip string or null
|
||||
*/
|
||||
public final String getTooltip() {
|
||||
return mTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiTextAttributeNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiTextAttributeNode(this, uiParent);
|
||||
}
|
||||
|
||||
// ------- IPropertyDescriptor Methods
|
||||
|
||||
public CellEditor createPropertyEditor(Composite parent) {
|
||||
return new TextValueCellEditor(parent);
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
ElementDescriptor parent = getParent();
|
||||
if (parent != null) {
|
||||
return parent.getUiName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mTooltip;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return mUiName;
|
||||
}
|
||||
|
||||
public String[] getFilterFlags() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getHelpContextIds() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getId() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ILabelProvider getLabelProvider() {
|
||||
return AttributeDescriptorLabelProvider.getProvider();
|
||||
}
|
||||
|
||||
public boolean isCompatibleWith(IPropertyDescriptor anotherProperty) {
|
||||
return anotherProperty == this;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiTextValueNode;
|
||||
|
||||
|
||||
/**
|
||||
* Describes the value of an XML element.
|
||||
* <p/>
|
||||
* The value is a simple text string, displayed by an {@link UiTextValueNode}.
|
||||
*/
|
||||
public class TextValueDescriptor extends TextAttributeDescriptor {
|
||||
|
||||
/**
|
||||
* Creates a new {@link TextValueDescriptor}
|
||||
*
|
||||
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
|
||||
* @param tooltip A non-empty tooltip string or null
|
||||
*/
|
||||
public TextValueDescriptor(String uiName, String tooltip) {
|
||||
super("#text" /* xmlLocalName */, uiName, null /* nsUri */, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiTextValueNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiTextValueNode(this, uiParent);
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
|
||||
/**
|
||||
* Describes an XMLNS attribute that is hidden.
|
||||
* <p/>
|
||||
* Such an attribute has no user interface and no corresponding {@link UiAttributeNode}.
|
||||
* It also has a single constant default value.
|
||||
* <p/>
|
||||
* When loading an XML, we'll ignore this attribute.
|
||||
* However when writing a new XML, we should always write this attribute.
|
||||
* <p/>
|
||||
* Currently this is used for the xmlns:android attribute in the manifest element.
|
||||
*/
|
||||
public final class XmlnsAttributeDescriptor extends AttributeDescriptor {
|
||||
|
||||
/**
|
||||
* URI of the reserved "xmlns" prefix, as described in
|
||||
* http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#normalizeDocumentAlgo
|
||||
*/
|
||||
public final static String XMLNS_URI = "http://www.w3.org/2000/xmlns/"; //$NON-NLS-1$
|
||||
|
||||
private String mValue;
|
||||
|
||||
|
||||
public XmlnsAttributeDescriptor(String defaultPrefix, String value) {
|
||||
super(defaultPrefix, XMLNS_URI);
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this specialized attribute descriptor, which is the URI associated
|
||||
* to the declared namespace prefix.
|
||||
*/
|
||||
public String getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "xmlns" prefix that is always used by this node for its namespace URI.
|
||||
* This is defined by the XML specification.
|
||||
*/
|
||||
public String getXmlNsPrefix() {
|
||||
return "xmlns"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fully-qualified attribute name, namely "xmlns:xxx" where xxx is
|
||||
* the defaultPrefix passed in the constructor.
|
||||
*/
|
||||
public String getXmlNsName() {
|
||||
return getXmlNsPrefix() + ":" + getXmlLocalName(); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Always returns null. {@link XmlnsAttributeDescriptor} has no user interface.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.layout;
|
||||
|
||||
import com.android.ide.eclipse.editors.AndroidContentAssist;
|
||||
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
|
||||
|
||||
/**
|
||||
* Content Assist Processor for /res/layout XML files
|
||||
*/
|
||||
class LayoutContentAssist extends AndroidContentAssist {
|
||||
|
||||
/**
|
||||
* Constructor for LayoutContentAssist
|
||||
*/
|
||||
public LayoutContentAssist() {
|
||||
super(LayoutDescriptors.getInstance().getDescriptor().getChildren());
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout;
|
||||
|
||||
import com.android.ide.eclipse.editors.IconFactory;
|
||||
import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
|
||||
import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
|
||||
import com.android.ide.eclipse.editors.wizards.ConfigurationSelector;
|
||||
import com.android.ide.eclipse.editors.wizards.ConfigurationSelector.ConfigurationState;
|
||||
|
||||
import org.eclipse.jface.dialogs.IDialogConstants;
|
||||
import org.eclipse.jface.dialogs.TrayDialog;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
|
||||
/**
|
||||
* Dialog to choose a non existing {@link FolderConfiguration}.
|
||||
*/
|
||||
class LayoutCreatorDialog extends TrayDialog {
|
||||
|
||||
private ConfigurationSelector mSelector;
|
||||
private Composite mStatusComposite;
|
||||
private Label mStatusLabel;
|
||||
private Label mStatusImage;
|
||||
|
||||
private final FolderConfiguration mConfig = new FolderConfiguration();
|
||||
private final String mFileName;
|
||||
|
||||
/**
|
||||
* Creates a dialog, and init the UI from a {@link FolderConfiguration}.
|
||||
* @param parentShell the parent {@link Shell}.
|
||||
* @param config The starting configuration.
|
||||
*/
|
||||
LayoutCreatorDialog(Shell parentShell, String fileName, FolderConfiguration config) {
|
||||
super(parentShell);
|
||||
|
||||
mFileName = fileName;
|
||||
// FIXME: add some data to know what configurations already exist.
|
||||
mConfig.set(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Control createDialogArea(Composite parent) {
|
||||
Composite top = new Composite(parent, SWT.NONE);
|
||||
top.setLayoutData(new GridData());
|
||||
top.setLayout(new GridLayout(1, false));
|
||||
|
||||
new Label(top, SWT.NONE).setText(
|
||||
String.format("Configuration for the alternate version of %1$s", mFileName));
|
||||
|
||||
mSelector = new ConfigurationSelector(top);
|
||||
mSelector.setConfiguration(mConfig);
|
||||
|
||||
// parent's layout is a GridLayout as specified in the javadoc.
|
||||
GridData gd = new GridData();
|
||||
gd.widthHint = ConfigurationSelector.WIDTH_HINT;
|
||||
gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
|
||||
mSelector.setLayoutData(gd);
|
||||
|
||||
// add a listener to check on the validity of the FolderConfiguration as
|
||||
// they are built.
|
||||
mSelector.setOnChangeListener(new Runnable() {
|
||||
public void run() {
|
||||
ConfigurationState state = mSelector.getState();
|
||||
|
||||
switch (state) {
|
||||
case OK:
|
||||
mSelector.getConfiguration(mConfig);
|
||||
|
||||
resetStatus();
|
||||
mStatusImage.setImage(null);
|
||||
getButton(IDialogConstants.OK_ID).setEnabled(true);
|
||||
break;
|
||||
case INVALID_CONFIG:
|
||||
ResourceQualifier invalidQualifier = mSelector.getInvalidQualifier();
|
||||
mStatusLabel.setText(String.format(
|
||||
"Invalid Configuration: %1$s has no filter set.",
|
||||
invalidQualifier.getName()));
|
||||
mStatusImage.setImage(IconFactory.getInstance().getIcon("warning")); //$NON-NLS-1$
|
||||
getButton(IDialogConstants.OK_ID).setEnabled(false);
|
||||
break;
|
||||
case REGION_WITHOUT_LANGUAGE:
|
||||
mStatusLabel.setText(
|
||||
"The Region qualifier requires the Language qualifier.");
|
||||
mStatusImage.setImage(IconFactory.getInstance().getIcon("warning")); //$NON-NLS-1$
|
||||
getButton(IDialogConstants.OK_ID).setEnabled(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// need to relayout, because of the change in size in mErrorImage.
|
||||
mStatusComposite.layout();
|
||||
}
|
||||
});
|
||||
|
||||
mStatusComposite = new Composite(top, SWT.NONE);
|
||||
mStatusComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
GridLayout gl = new GridLayout(2, false);
|
||||
mStatusComposite.setLayout(gl);
|
||||
gl.marginHeight = gl.marginWidth = 0;
|
||||
|
||||
mStatusImage = new Label(mStatusComposite, SWT.NONE);
|
||||
mStatusLabel = new Label(mStatusComposite, SWT.NONE);
|
||||
mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
resetStatus();
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
public void getConfiguration(FolderConfiguration config) {
|
||||
config.set(mConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* resets the status label to show the file that will be created.
|
||||
*/
|
||||
private void resetStatus() {
|
||||
mStatusLabel.setText(String.format("New File: res/%1$s/%2$s",
|
||||
mConfig.getFolderName(ResourceFolderType.LAYOUT), mFileName));
|
||||
}
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.layout;
|
||||
|
||||
import com.android.ide.eclipse.common.EclipseUiHelper;
|
||||
import com.android.ide.eclipse.editors.AndroidEditor;
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor;
|
||||
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.gef.ui.parts.TreeViewer;
|
||||
import org.eclipse.ui.IEditorInput;
|
||||
import org.eclipse.ui.IEditorPart;
|
||||
import org.eclipse.ui.IPartListener;
|
||||
import org.eclipse.ui.IShowEditorInput;
|
||||
import org.eclipse.ui.IWorkbenchPage;
|
||||
import org.eclipse.ui.IWorkbenchPart;
|
||||
import org.eclipse.ui.IWorkbenchPartSite;
|
||||
import org.eclipse.ui.PartInitException;
|
||||
import org.eclipse.ui.part.FileEditorInput;
|
||||
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
|
||||
import org.eclipse.ui.views.properties.IPropertySheetPage;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
* Multi-page form editor for /res/layout XML files.
|
||||
*/
|
||||
public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPartListener {
|
||||
|
||||
public static final String ID = "com.android.ide.eclipse.editors.layout.LayoutEditor"; //$NON-NLS-1$
|
||||
|
||||
/** Root node of the UI element hierarchy */
|
||||
private UiDocumentNode mUiRootNode;
|
||||
/** Listener to update the root node if the resource framework changes */
|
||||
private Runnable mResourceRefreshListener;
|
||||
|
||||
private GraphicalLayoutEditor mGraphicalEditor;
|
||||
private int mGraphicalEditorIndex;
|
||||
/** Implementation of the {@link IContentOutlinePage} for this editor */
|
||||
private UiContentOutlinePage mOutline;
|
||||
/** Custom implementation of {@link IPropertySheetPage} for this editor */
|
||||
private UiPropertySheetPage mPropertyPage;
|
||||
|
||||
|
||||
/**
|
||||
* Creates the form editor for resources XML files.
|
||||
*/
|
||||
public LayoutEditor() {
|
||||
super();
|
||||
initUiRootNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The root node of the UI element hierarchy
|
||||
*/
|
||||
@Override
|
||||
public UiDocumentNode getUiRootNode() {
|
||||
return mUiRootNode;
|
||||
}
|
||||
|
||||
// ---- Base Class Overrides ----
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (mResourceRefreshListener != null) {
|
||||
EditorsPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener);
|
||||
mResourceRefreshListener = null;
|
||||
}
|
||||
getSite().getPage().removePartListener(this);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the XML.
|
||||
* <p/>
|
||||
* The actual save operation is done in the super class by committing
|
||||
* all data to the XML model and then having the Structured XML Editor
|
||||
* save the XML.
|
||||
* <p/>
|
||||
* Here we just need to tell the graphical editor that the model has
|
||||
* been saved.
|
||||
*/
|
||||
@Override
|
||||
public void doSave(IProgressMonitor monitor) {
|
||||
super.doSave(monitor);
|
||||
if (mGraphicalEditor != null) {
|
||||
mGraphicalEditor.doSave(monitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the "save as" operation is supported by this editor.
|
||||
* <p/>
|
||||
* Save-As is a valid operation for the ManifestEditor since it acts on a
|
||||
* single source file.
|
||||
*
|
||||
* @see IEditorPart
|
||||
*/
|
||||
@Override
|
||||
public boolean isSaveAsAllowed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the various form pages.
|
||||
*/
|
||||
@Override
|
||||
protected void createFormPages() {
|
||||
try {
|
||||
// The graphical layout editor is now enabled by default.
|
||||
// In case there's an issue we provide a way to disable it using an
|
||||
// env variable.
|
||||
if (System.getenv("ANDROID_DISABLE_LAYOUT") == null) {
|
||||
if (mGraphicalEditor == null) {
|
||||
mGraphicalEditor = new GraphicalLayoutEditor(this);
|
||||
mGraphicalEditorIndex = addPage(mGraphicalEditor, getEditorInput());
|
||||
setPageText(mGraphicalEditorIndex, mGraphicalEditor.getTitle());
|
||||
} else {
|
||||
mGraphicalEditor.reloadEditor();
|
||||
}
|
||||
|
||||
// update the config based on the opened file.
|
||||
IEditorInput input = getEditorInput();
|
||||
if (input instanceof FileEditorInput) {
|
||||
FileEditorInput fileInput = (FileEditorInput)input;
|
||||
ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(
|
||||
fileInput.getFile());
|
||||
if (resFolder != null) {
|
||||
mGraphicalEditor.editNewFile(resFolder.getConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
// put in place the listener to handle layout recompute only when needed.
|
||||
getSite().getPage().addPartListener(this);
|
||||
}
|
||||
} catch (PartInitException e) {
|
||||
EditorsPlugin.log(e, "Error creating nested page"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-java doc)
|
||||
* Change the tab/title name to include the name of the layout.
|
||||
*/
|
||||
@Override
|
||||
protected void setInput(IEditorInput input) {
|
||||
super.setInput(input);
|
||||
handleNewInput(input);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.eclipse.ui.part.EditorPart#setInputWithNotify(org.eclipse.ui.IEditorInput)
|
||||
*/
|
||||
@Override
|
||||
protected void setInputWithNotify(IEditorInput input) {
|
||||
super.setInputWithNotify(input);
|
||||
handleNewInput(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to replace the current {@link IEditorInput} with another one.
|
||||
* <p/>This is used when {@link MatchingStrategy} returned <code>true</code> which means we're
|
||||
* opening a different configuration of the same layout.
|
||||
*/
|
||||
public void showEditorInput(IEditorInput editorInput) {
|
||||
// save the current editor input.
|
||||
doSave(new NullProgressMonitor());
|
||||
|
||||
// get the current page
|
||||
int currentPage = getActivePage();
|
||||
|
||||
// remove the pages, except for the graphical editor, which will be dynamically adapted
|
||||
// to the new model.
|
||||
// page after the graphical editor:
|
||||
int count = getPageCount();
|
||||
for (int i = count - 1 ; i > mGraphicalEditorIndex ; i--) {
|
||||
removePage(i);
|
||||
}
|
||||
// pages before the graphical editor
|
||||
for (int i = mGraphicalEditorIndex - 1 ; i >= 0 ; i--) {
|
||||
removePage(i);
|
||||
}
|
||||
|
||||
// set the current input.
|
||||
setInputWithNotify(editorInput);
|
||||
|
||||
// re-create or reload the pages with the default page shown as the previous active page.
|
||||
createAndroidPages();
|
||||
selectDefaultPage(Integer.toString(currentPage));
|
||||
|
||||
// update the outline
|
||||
if (mOutline != null && mGraphicalEditor != null) {
|
||||
mOutline.reloadModel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the new XML Model, which XML root node is given.
|
||||
*
|
||||
* @param xml_doc The XML document, if available, or null if none exists.
|
||||
*/
|
||||
@Override
|
||||
protected void xmlModelChanged(Document xml_doc) {
|
||||
mUiRootNode.loadFromXmlNode(xml_doc);
|
||||
|
||||
// update the model first, since it is used by the viewers.
|
||||
super.xmlModelChanged(xml_doc);
|
||||
|
||||
if (mGraphicalEditor != null) {
|
||||
mGraphicalEditor.onXmlModelChanged();
|
||||
}
|
||||
|
||||
if (mOutline != null) {
|
||||
mOutline.reloadModel();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-java doc)
|
||||
* Returns the IContentOutlinePage when asked for it.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object getAdapter(Class adapter) {
|
||||
// for the outline, force it to come from the Graphical Editor.
|
||||
// This fixes the case where a layout file is opened in XML view first and the outline
|
||||
// gets stuck in the XML outline.
|
||||
if (IContentOutlinePage.class == adapter && mGraphicalEditor != null) {
|
||||
if (mOutline == null) {
|
||||
mOutline = new UiContentOutlinePage(mGraphicalEditor, new TreeViewer());
|
||||
}
|
||||
|
||||
return mOutline;
|
||||
}
|
||||
|
||||
if (IPropertySheetPage.class == adapter && mGraphicalEditor != null) {
|
||||
if (mPropertyPage == null) {
|
||||
mPropertyPage = new UiPropertySheetPage();
|
||||
}
|
||||
|
||||
return mPropertyPage;
|
||||
}
|
||||
|
||||
// return default
|
||||
return super.getAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void pageChange(int newPageIndex) {
|
||||
super.pageChange(newPageIndex);
|
||||
|
||||
if (mGraphicalEditor != null && newPageIndex == mGraphicalEditorIndex) {
|
||||
mGraphicalEditor.activated();
|
||||
}
|
||||
}
|
||||
|
||||
// ----- IPartListener Methods ----
|
||||
|
||||
public void partActivated(IWorkbenchPart part) {
|
||||
if (part == this) {
|
||||
if (mGraphicalEditor != null && getActivePage() == mGraphicalEditorIndex) {
|
||||
mGraphicalEditor.activated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void partBroughtToTop(IWorkbenchPart part) {
|
||||
partActivated(part);
|
||||
}
|
||||
|
||||
public void partClosed(IWorkbenchPart part) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void partDeactivated(IWorkbenchPart part) {
|
||||
// pass
|
||||
}
|
||||
|
||||
public void partOpened(IWorkbenchPart part) {
|
||||
EclipseUiHelper.showView(EclipseUiHelper.CONTENT_OUTLINE_VIEW_ID, false /* activate */);
|
||||
EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID, false /* activate */);
|
||||
}
|
||||
|
||||
// ---- Local Methods ----
|
||||
|
||||
/**
|
||||
* Returns true if the Graphics editor page is visible.
|
||||
* This <b>must</b> be called from the UI thread.
|
||||
*/
|
||||
boolean isGraphicalEditorActive() {
|
||||
IWorkbenchPartSite workbenchSite = getSite();
|
||||
IWorkbenchPage workbenchPage = workbenchSite.getPage();
|
||||
|
||||
// check if the editor is visible in the workbench page
|
||||
if (workbenchPage.isPartVisible(this)) {
|
||||
// and then if the page of the editor is visible (not to be confused with
|
||||
// the workbench page)
|
||||
return mGraphicalEditorIndex == getActivePage();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the initial UI Root Node, including the known mandatory elements.
|
||||
*/
|
||||
private void initUiRootNode() {
|
||||
// The root UI node is always created, even if there's no corresponding XML node.
|
||||
if (mUiRootNode == null) {
|
||||
DocumentDescriptor desc = LayoutDescriptors.getInstance().getDescriptor();
|
||||
mUiRootNode = (UiDocumentNode) desc.createUiNode();
|
||||
mUiRootNode.setEditor(this);
|
||||
|
||||
// Add a listener to refresh the root node if the resource framework changes
|
||||
// by forcing it to parse its own XML
|
||||
mResourceRefreshListener = new Runnable() {
|
||||
public void run() {
|
||||
commitPages(false /* onSave */);
|
||||
|
||||
mUiRootNode.reloadFromXmlNode(mUiRootNode.getXmlDocument());
|
||||
|
||||
if (mOutline != null) {
|
||||
mOutline.reloadModel();
|
||||
}
|
||||
|
||||
if (mGraphicalEditor != null) {
|
||||
mGraphicalEditor.recomputeLayout();
|
||||
}
|
||||
}
|
||||
};
|
||||
EditorsPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener);
|
||||
mResourceRefreshListener.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles a new input, and update the part name.
|
||||
* @param input the new input.
|
||||
*/
|
||||
private void handleNewInput(IEditorInput input) {
|
||||
if (input instanceof FileEditorInput) {
|
||||
FileEditorInput fileInput = (FileEditorInput) input;
|
||||
IFile file = fileInput.getFile();
|
||||
setPartName(String.format("%1$s",
|
||||
file.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IMarkerDelta;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Monitor for file changes triggering a layout redraw.
|
||||
*/
|
||||
public final class LayoutReloadMonitor implements IFileListener, IResourceEventListener {
|
||||
|
||||
// singleton, enforced by private constructor.
|
||||
private final static LayoutReloadMonitor sThis = new LayoutReloadMonitor();
|
||||
|
||||
/**
|
||||
* Map of listeners by IProject.
|
||||
*/
|
||||
private final Map<IProject, List<ILayoutReloadListener>> mListenerMap =
|
||||
new HashMap<IProject, List<ILayoutReloadListener>>();
|
||||
|
||||
private final static int CHANGE_CODE = 0;
|
||||
private final static int CHANGE_RESOURCES = 1;
|
||||
private final static int CHANGE_R = 2;
|
||||
private final static int CHANGE_COUNT = 3;
|
||||
/**
|
||||
* List of projects having received a file change. the boolean[] contains 3 values:
|
||||
* <ul><li>CHANGE_CODE: code change flag.</li>
|
||||
* <li>CHANGE_RESOURCES: resource change flag.</li>
|
||||
* <li>CHANGE_R: R clas change flag</li></ul>
|
||||
*/
|
||||
private final Map<IProject, boolean[]> mChangedProjects = new HashMap<IProject, boolean[]>();
|
||||
|
||||
/**
|
||||
* Classes which implement this interface provide a method to respond to resource changes
|
||||
* triggering a layout redraw
|
||||
*/
|
||||
public interface ILayoutReloadListener {
|
||||
/**
|
||||
* Sent when the layout needs to be redrawn
|
||||
* @param codeChange The trigger happened due to a code change.
|
||||
* @param rChange The trigger happened due to a change in the R class.
|
||||
* @param resChange The trigger happened due to a resource change.
|
||||
*/
|
||||
void reloadLayout(boolean codeChange, boolean rChange, boolean resChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the single instance of {@link LayoutReloadMonitor}.
|
||||
*/
|
||||
public static LayoutReloadMonitor getMonitor() {
|
||||
return sThis;
|
||||
}
|
||||
|
||||
private LayoutReloadMonitor() {
|
||||
ResourceMonitor monitor = ResourceMonitor.getMonitor();
|
||||
monitor.addFileListener(this, IResourceDelta.ADDED | IResourceDelta.CHANGED);
|
||||
monitor.addResourceEventListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for a given {@link IProject}.
|
||||
* @param project
|
||||
* @param listener
|
||||
*/
|
||||
public void addListener(IProject project, ILayoutReloadListener listener) {
|
||||
synchronized (mListenerMap) {
|
||||
List<ILayoutReloadListener> list = mListenerMap.get(project);
|
||||
if (list == null) {
|
||||
list = new ArrayList<ILayoutReloadListener>();
|
||||
mListenerMap.put(project, list);
|
||||
}
|
||||
|
||||
list.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for a given {@link IProject}.
|
||||
* @param project
|
||||
* @param listener
|
||||
*/
|
||||
public void removeListener(IProject project, ILayoutReloadListener listener) {
|
||||
synchronized (mListenerMap) {
|
||||
List<ILayoutReloadListener> list = mListenerMap.get(project);
|
||||
if (list != null) {
|
||||
list.remove(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener#fileChanged(org.eclipse.core.resources.IFile, org.eclipse.core.resources.IMarkerDelta[], int)
|
||||
*
|
||||
* Callback for ResourceMonitor.IFileListener. Called when a file changed.
|
||||
* This records the changes for each project, but does not notify listeners.
|
||||
* @see #resourceChangeEventEnd
|
||||
*/
|
||||
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
|
||||
// get the file project
|
||||
IProject project = file.getProject();
|
||||
|
||||
// if this project has already been marked as modified, we do nothing.
|
||||
boolean[] changeFlags = mChangedProjects.get(project);
|
||||
if (changeFlags != null && changeFlags[CHANGE_CODE] && changeFlags[CHANGE_RESOURCES] &&
|
||||
changeFlags[CHANGE_R]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// now check that the file is *NOT* a layout file (those automatically trigger a layout
|
||||
// reload and we don't want to do it twice.
|
||||
ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(file);
|
||||
if (resFolder != null) {
|
||||
if (resFolder.getType() != ResourceFolderType.LAYOUT) {
|
||||
// this is a resource change!
|
||||
if (changeFlags == null) {
|
||||
changeFlags = new boolean[CHANGE_COUNT];
|
||||
mChangedProjects.put(project, changeFlags);
|
||||
}
|
||||
|
||||
changeFlags[CHANGE_RESOURCES] = true;
|
||||
}
|
||||
} else if (AndroidConstants.EXT_CLASS.equals(file.getFileExtension())) {
|
||||
if (file.getName().matches("R[\\$\\.](.*)")) {
|
||||
// this is a R change!
|
||||
if (changeFlags == null) {
|
||||
changeFlags = new boolean[CHANGE_COUNT];
|
||||
mChangedProjects.put(project, changeFlags);
|
||||
}
|
||||
|
||||
changeFlags[CHANGE_R] = true;
|
||||
} else {
|
||||
// this is a code change!
|
||||
if (changeFlags == null) {
|
||||
changeFlags = new boolean[CHANGE_COUNT];
|
||||
mChangedProjects.put(project, changeFlags);
|
||||
}
|
||||
|
||||
changeFlags[CHANGE_CODE] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener#resourceChangeEventStart()
|
||||
*
|
||||
* Callback for ResourceMonitor.IResourceEventListener. Called at the beginning of a resource
|
||||
* change event. This is called once, while fileChanged can be called several times.
|
||||
*
|
||||
*/
|
||||
public void resourceChangeEventStart() {
|
||||
// nothing to be done here, it all happens in the resourceChangeEventEnd
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IResourceEventListener#resourceChangeEventEnd()
|
||||
*
|
||||
* Callback for ResourceMonitor.IResourceEventListener. Called at the end of a resource
|
||||
* change event. This is where we notify the listeners.
|
||||
*/
|
||||
public void resourceChangeEventEnd() {
|
||||
// for each IProject that was changed, we notify all the listeners.
|
||||
synchronized (mListenerMap) {
|
||||
for (Entry<IProject, boolean[]> project : mChangedProjects.entrySet()) {
|
||||
List<ILayoutReloadListener> listeners = mListenerMap.get(project.getKey());
|
||||
|
||||
boolean[] flags = project.getValue();
|
||||
|
||||
if (listeners != null) {
|
||||
for (ILayoutReloadListener listener : listeners) {
|
||||
listener.reloadLayout(flags[CHANGE_CODE], flags[CHANGE_R],
|
||||
flags[CHANGE_RESOURCES]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// empty the list.
|
||||
mChangedProjects.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout;
|
||||
|
||||
|
||||
import com.android.ide.eclipse.editors.AndroidSourceViewerConfig;
|
||||
|
||||
/**
|
||||
* Source Viewer Configuration that calls in LayoutContentAssist.
|
||||
*/
|
||||
public class LayoutSourceViewerConfig extends AndroidSourceViewerConfig {
|
||||
|
||||
public LayoutSourceViewerConfig() {
|
||||
super(new LayoutContentAssist());
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout;
|
||||
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
|
||||
import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.ui.IEditorInput;
|
||||
import org.eclipse.ui.forms.IManagedForm;
|
||||
import org.eclipse.ui.forms.editor.FormPage;
|
||||
import org.eclipse.ui.forms.widgets.ScrolledForm;
|
||||
import org.eclipse.ui.part.FileEditorInput;
|
||||
|
||||
/**
|
||||
* Page for the layout tree-based form editor.
|
||||
*
|
||||
* @deprecated This is being phased out. We use the GraphicalLayoutEditor instead.
|
||||
*/
|
||||
public final class LayoutTreePage extends FormPage {
|
||||
/** Page id used for switching tabs programmatically */
|
||||
public final static String PAGE_ID = "layout_tree_page"; //$NON-NLS-1$
|
||||
|
||||
/** Container editor */
|
||||
LayoutEditor mEditor;
|
||||
|
||||
public LayoutTreePage(LayoutEditor editor) {
|
||||
super(editor, PAGE_ID, "Layout"); // tab's label, 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();
|
||||
|
||||
String configText = null;
|
||||
IEditorInput input = mEditor.getEditorInput();
|
||||
if (input instanceof FileEditorInput) {
|
||||
FileEditorInput fileInput = (FileEditorInput)input;
|
||||
IFile iFile = fileInput.getFile();
|
||||
|
||||
ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(iFile);
|
||||
if (resFolder != null) {
|
||||
configText = resFolder.getConfiguration().toDisplayString();
|
||||
}
|
||||
}
|
||||
|
||||
if (configText != null) {
|
||||
form.setText(String.format("Android Layout (%1$s)", configText));
|
||||
} else {
|
||||
form.setText("Android Layout");
|
||||
}
|
||||
|
||||
form.setImage(EditorsPlugin.getAndroidLogo());
|
||||
|
||||
UiElementNode rootNode = mEditor.getUiRootNode();
|
||||
UiTreeBlock block = new UiTreeBlock(mEditor, rootNode,
|
||||
true /* autoCreateRoot */,
|
||||
null /* no element filters */,
|
||||
"Layout Elements",
|
||||
"List of all layout elements in this XML file.");
|
||||
block.createContent(managedForm);
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout;
|
||||
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolder;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceFolderType;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceManager;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.ui.IEditorInput;
|
||||
import org.eclipse.ui.IEditorMatchingStrategy;
|
||||
import org.eclipse.ui.IEditorReference;
|
||||
import org.eclipse.ui.PartInitException;
|
||||
import org.eclipse.ui.part.FileEditorInput;
|
||||
|
||||
/**
|
||||
* Matching strategy for the Layout Editor. This is used to open all configurations of a layout
|
||||
* in the same editor.
|
||||
*/
|
||||
public class MatchingStrategy implements IEditorMatchingStrategy {
|
||||
|
||||
public boolean matches(IEditorReference editorRef, IEditorInput input) {
|
||||
// first check that the file being opened is a layout file.
|
||||
if (input instanceof FileEditorInput) {
|
||||
FileEditorInput fileInput = (FileEditorInput)input;
|
||||
|
||||
// get the IFile object and check it's in one of the layout folders.
|
||||
IFile iFile = fileInput.getFile();
|
||||
ResourceFolder resFolder = ResourceManager.getInstance().getResourceFolder(iFile);
|
||||
|
||||
// if it's a layout, we know check the name of the fileInput against the name of the
|
||||
// file being currently edited by the editor since those are independent of the config.
|
||||
if (resFolder != null && resFolder.getType() == ResourceFolderType.LAYOUT) {
|
||||
try {
|
||||
IEditorInput editorInput = editorRef.getEditorInput();
|
||||
if (editorInput instanceof FileEditorInput) {
|
||||
FileEditorInput editorFileInput = (FileEditorInput)editorInput;
|
||||
IFile editorIFile = editorFileInput.getFile();
|
||||
|
||||
return editorIFile.getProject().equals(iFile.getProject())
|
||||
&& editorIFile.getName().equals(iFile.getName());
|
||||
}
|
||||
} catch (PartInitException e) {
|
||||
// we do nothing, we'll just return false.
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout;
|
||||
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
|
||||
|
||||
import org.eclipse.gef.palette.CombinedTemplateCreationEntry;
|
||||
import org.eclipse.gef.palette.MarqueeToolEntry;
|
||||
import org.eclipse.gef.palette.PaletteDrawer;
|
||||
import org.eclipse.gef.palette.PaletteGroup;
|
||||
import org.eclipse.gef.palette.PaletteRoot;
|
||||
import org.eclipse.gef.palette.PanningSelectionToolEntry;
|
||||
import org.eclipse.gef.requests.CreationFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Factory that creates the palette for the {@link GraphicalLayoutEditor}.
|
||||
*/
|
||||
public class PaletteFactory {
|
||||
|
||||
private static PaletteRoot sPaletteRoot;
|
||||
private static Runnable sSdkChangedListener;
|
||||
|
||||
/** Static factory, nothing to instantiate here. */
|
||||
private PaletteFactory() {
|
||||
}
|
||||
|
||||
public static PaletteRoot createPaletteRoot() {
|
||||
if (sSdkChangedListener == null) {
|
||||
sSdkChangedListener = new Runnable() {
|
||||
public void run() {
|
||||
if (sPaletteRoot != null) {
|
||||
// The SDK has changed. Remove the drawer groups and rebuild them.
|
||||
for (int n = sPaletteRoot.getChildren().size() - 1; n >= 0; n--) {
|
||||
sPaletteRoot.getChildren().remove(n);
|
||||
}
|
||||
|
||||
addTools(sPaletteRoot);
|
||||
addViews(sPaletteRoot, "Layouts",
|
||||
LayoutDescriptors.getInstance().getLayoutDescriptors());
|
||||
addViews(sPaletteRoot, "Views",
|
||||
LayoutDescriptors.getInstance().getViewDescriptors());
|
||||
}
|
||||
}
|
||||
};
|
||||
EditorsPlugin.getDefault().addResourceChangedListener(sSdkChangedListener);
|
||||
}
|
||||
|
||||
if (sPaletteRoot == null) {
|
||||
sPaletteRoot = new PaletteRoot();
|
||||
sSdkChangedListener.run();
|
||||
}
|
||||
return sPaletteRoot;
|
||||
}
|
||||
|
||||
private static void addTools(PaletteRoot paletteRoot) {
|
||||
PaletteGroup group = new PaletteGroup("Tools");
|
||||
|
||||
// Default tools: selection and marquee selection
|
||||
PanningSelectionToolEntry entry = new PanningSelectionToolEntry();
|
||||
group.add(entry);
|
||||
paletteRoot.setDefaultEntry(entry);
|
||||
|
||||
group.add(new MarqueeToolEntry());
|
||||
|
||||
paletteRoot.add(group);
|
||||
}
|
||||
|
||||
private static void addViews(PaletteRoot paletteRoot, String groupName,
|
||||
List<ElementDescriptor> descriptors) {
|
||||
PaletteDrawer group = new PaletteDrawer(groupName);
|
||||
|
||||
for (ElementDescriptor desc : descriptors) {
|
||||
CombinedTemplateCreationEntry entry = new CombinedTemplateCreationEntry(
|
||||
desc.getUiName(), // label
|
||||
desc.getTooltip(), // short description
|
||||
desc.getClass(), // template
|
||||
new ElementFactory(desc), // factory
|
||||
desc.getImageDescriptor(), // small icon
|
||||
desc.getImageDescriptor() // large icon
|
||||
);
|
||||
group.add(entry);
|
||||
}
|
||||
|
||||
paletteRoot.add(group);
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
|
||||
public static class ElementFactory implements CreationFactory {
|
||||
|
||||
private final ElementDescriptor mDescriptor;
|
||||
|
||||
public ElementFactory(ElementDescriptor descriptor) {
|
||||
mDescriptor = descriptor;
|
||||
}
|
||||
|
||||
public Object getNewObject() {
|
||||
return mDescriptor;
|
||||
}
|
||||
|
||||
public Object getObjectType() {
|
||||
return mDescriptor;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ProjectClassLoader;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ProjectResources;
|
||||
import com.android.layoutlib.api.IProjectCallback;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Loader for Android Project class in order to use them in the layout editor.
|
||||
*/
|
||||
public final class ProjectCallback implements IProjectCallback {
|
||||
|
||||
private final HashMap<String, Class<?>> mLoadedClasses = new HashMap<String, Class<?>>();
|
||||
private final IProject mProject;
|
||||
private final ClassLoader mParentClassLoader;
|
||||
private final ProjectResources mProjectRes;
|
||||
private boolean mUsed = false;
|
||||
private String mNamespace;
|
||||
|
||||
ProjectCallback(ClassLoader classLoader, ProjectResources projectRes, IProject project) {
|
||||
mParentClassLoader = classLoader;
|
||||
mProjectRes = projectRes;
|
||||
mProject = project;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation goes through the output directory of the Eclipse project and loads the
|
||||
* <code>.class</code> file directly.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object loadView(String className, Class[] constructorSignature,
|
||||
Object[] constructorParameters)
|
||||
throws ClassNotFoundException, Exception {
|
||||
|
||||
// look for a cached version
|
||||
Class<?> clazz = mLoadedClasses.get(className);
|
||||
if (clazz != null) {
|
||||
return instantiateClass(clazz, constructorSignature, constructorParameters);
|
||||
}
|
||||
|
||||
// load the class.
|
||||
ProjectClassLoader loader = new ProjectClassLoader(mParentClassLoader, mProject);
|
||||
clazz = loader.loadClass(className);
|
||||
|
||||
if (clazz != null) {
|
||||
mUsed = true;
|
||||
mLoadedClasses.put(className, clazz);
|
||||
return instantiateClass(clazz, constructorSignature, constructorParameters);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* Returns the namespace for the project. The namespace contains a standard part + the
|
||||
* application package.
|
||||
*/
|
||||
public String getNamespace() {
|
||||
if (mNamespace == null) {
|
||||
AndroidManifestHelper manifest = new AndroidManifestHelper(mProject);
|
||||
String javaPackage = manifest.getPackageName();
|
||||
|
||||
mNamespace = String.format(AndroidConstants.NS_CUSTOM_RESOURCES, javaPackage);
|
||||
}
|
||||
|
||||
return mNamespace;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.android.layoutlib.api.IProjectCallback#resolveResourceValue(int)
|
||||
*/
|
||||
public String[] resolveResourceValue(int id) {
|
||||
if (mProjectRes != null) {
|
||||
return mProjectRes.resolveResourceValue(id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.android.layoutlib.api.IProjectCallback#resolveResourceValue(int[])
|
||||
*/
|
||||
public String resolveResourceValue(int[] id) {
|
||||
if (mProjectRes != null) {
|
||||
return mProjectRes.resolveResourceValue(id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.android.layoutlib.api.IProjectCallback#getResourceValue(java.lang.String, java.lang.String)
|
||||
*/
|
||||
public Integer getResourceValue(String type, String name) {
|
||||
if (mProjectRes != null) {
|
||||
return mProjectRes.getResourceValue(type, name);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the loader has received requests to load custom views.
|
||||
* <p/>This allows to efficiently only recreate when needed upon code change in the project.
|
||||
*/
|
||||
boolean isUsed() {
|
||||
return mUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a class object, using a specific constructor and parameters.
|
||||
* @param clazz the class to instantiate
|
||||
* @param constructorSignature the signature of the constructor to use
|
||||
* @param constructorParameters the parameters to use in the constructor.
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object instantiateClass(Class<?> clazz, Class[] constructorSignature,
|
||||
Object[] constructorParameters) throws Exception {
|
||||
Constructor<?> constructor = clazz.getConstructor(constructorSignature);
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance(constructorParameters);
|
||||
}
|
||||
}
|
||||
@@ -1,568 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* 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.editors.layout;
|
||||
|
||||
import com.android.ide.eclipse.common.EclipseUiHelper;
|
||||
import com.android.ide.eclipse.editors.IconFactory;
|
||||
import com.android.ide.eclipse.editors.layout.parts.UiDocumentTreeEditPart;
|
||||
import com.android.ide.eclipse.editors.layout.parts.UiElementTreeEditPart;
|
||||
import com.android.ide.eclipse.editors.layout.parts.UiElementTreeEditPartFactory;
|
||||
import com.android.ide.eclipse.editors.layout.parts.UiLayoutTreeEditPart;
|
||||
import com.android.ide.eclipse.editors.layout.parts.UiViewTreeEditPart;
|
||||
import com.android.ide.eclipse.editors.ui.tree.CopyCutAction;
|
||||
import com.android.ide.eclipse.editors.ui.tree.PasteAction;
|
||||
import com.android.ide.eclipse.editors.ui.tree.UiActions;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.gef.EditPartViewer;
|
||||
import org.eclipse.gef.ui.parts.ContentOutlinePage;
|
||||
import org.eclipse.jface.action.Action;
|
||||
import org.eclipse.jface.action.IMenuListener;
|
||||
import org.eclipse.jface.action.IMenuManager;
|
||||
import org.eclipse.jface.action.IToolBarManager;
|
||||
import org.eclipse.jface.action.MenuManager;
|
||||
import org.eclipse.jface.action.Separator;
|
||||
import org.eclipse.jface.viewers.ISelection;
|
||||
import org.eclipse.jface.viewers.ISelectionChangedListener;
|
||||
import org.eclipse.jface.viewers.SelectionChangedEvent;
|
||||
import org.eclipse.jface.viewers.StructuredSelection;
|
||||
import org.eclipse.jface.viewers.TreePath;
|
||||
import org.eclipse.jface.viewers.TreeSelection;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.graphics.Point;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.layout.FillLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Event;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
import org.eclipse.swt.widgets.Menu;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
import org.eclipse.swt.widgets.Tree;
|
||||
import org.eclipse.swt.widgets.TreeItem;
|
||||
import org.eclipse.ui.IActionBars;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link ContentOutlinePage} to display {@link UiElementNode}.
|
||||
*/
|
||||
class UiContentOutlinePage extends ContentOutlinePage {
|
||||
|
||||
private GraphicalLayoutEditor mEditor;
|
||||
|
||||
private Action mAddAction;
|
||||
private Action mDeleteAction;
|
||||
private Action mUpAction;
|
||||
private Action mDownAction;
|
||||
|
||||
private UiOutlineActions mUiActions = new UiOutlineActions();
|
||||
|
||||
public UiContentOutlinePage(GraphicalLayoutEditor editor, final EditPartViewer viewer) {
|
||||
super(viewer);
|
||||
mEditor = editor;
|
||||
IconFactory factory = IconFactory.getInstance();
|
||||
|
||||
mAddAction = new Action("Add...") {
|
||||
@Override
|
||||
public void run() {
|
||||
UiElementNode node = getModelSelection();
|
||||
|
||||
mUiActions.doAdd(node, viewer.getControl().getShell());
|
||||
}
|
||||
};
|
||||
mAddAction.setToolTipText("Adds a new element.");
|
||||
mAddAction.setImageDescriptor(factory.getImageDescriptor("add")); //$NON-NLS-1$
|
||||
|
||||
mDeleteAction = new Action("Remove...") {
|
||||
@Override
|
||||
public void run() {
|
||||
UiElementNode node = getModelSelection();
|
||||
|
||||
mUiActions.doRemove(node, viewer.getControl().getShell());
|
||||
}
|
||||
};
|
||||
mDeleteAction.setToolTipText("Removes an existing selected element.");
|
||||
mDeleteAction.setImageDescriptor(factory.getImageDescriptor("delete")); //$NON-NLS-1$
|
||||
|
||||
mUpAction = new Action("Up") {
|
||||
@Override
|
||||
public void run() {
|
||||
UiElementNode node = getModelSelection();
|
||||
|
||||
mUiActions.doUp(node);
|
||||
}
|
||||
};
|
||||
mUpAction.setToolTipText("Moves the selected element up");
|
||||
mUpAction.setImageDescriptor(factory.getImageDescriptor("up")); //$NON-NLS-1$
|
||||
|
||||
mDownAction = new Action("Down") {
|
||||
@Override
|
||||
public void run() {
|
||||
UiElementNode node = getModelSelection();
|
||||
|
||||
mUiActions.doDown(node);
|
||||
}
|
||||
};
|
||||
mDownAction.setToolTipText("Moves the selected element down");
|
||||
mDownAction.setImageDescriptor(factory.getImageDescriptor("down")); //$NON-NLS-1$
|
||||
|
||||
// all actions disabled by default.
|
||||
mAddAction.setEnabled(false);
|
||||
mDeleteAction.setEnabled(false);
|
||||
mUpAction.setEnabled(false);
|
||||
mDownAction.setEnabled(false);
|
||||
|
||||
addSelectionChangedListener(new ISelectionChangedListener() {
|
||||
public void selectionChanged(SelectionChangedEvent event) {
|
||||
ISelection selection = event.getSelection();
|
||||
|
||||
// the selection is never empty. The least it'll contain is the
|
||||
// UiDocumentTreeEditPart object.
|
||||
if (selection instanceof StructuredSelection) {
|
||||
StructuredSelection structSel = (StructuredSelection)selection;
|
||||
|
||||
if (structSel.size() == 1 &&
|
||||
structSel.getFirstElement() instanceof UiDocumentTreeEditPart) {
|
||||
mDeleteAction.setEnabled(false);
|
||||
mUpAction.setEnabled(false);
|
||||
mDownAction.setEnabled(false);
|
||||
} else {
|
||||
mDeleteAction.setEnabled(true);
|
||||
mUpAction.setEnabled(true);
|
||||
mDownAction.setEnabled(true);
|
||||
}
|
||||
|
||||
// the "add" button is always enabled, in order to be able to set the
|
||||
// initial root node
|
||||
mAddAction.setEnabled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.ui.part.IPage#createControl(org.eclipse.swt.widgets.Composite)
|
||||
*/
|
||||
@Override
|
||||
public void createControl(Composite parent) {
|
||||
// create outline viewer page
|
||||
getViewer().createControl(parent);
|
||||
|
||||
// configure outline viewer
|
||||
getViewer().setEditPartFactory(new UiElementTreeEditPartFactory());
|
||||
|
||||
setupOutline();
|
||||
setupContextMenu();
|
||||
setupTooltip();
|
||||
setupDoubleClick();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.eclipse.ui.part.Page#setActionBars(org.eclipse.ui.IActionBars)
|
||||
*
|
||||
* Called automatically after createControl
|
||||
*/
|
||||
@Override
|
||||
public void setActionBars(IActionBars actionBars) {
|
||||
IToolBarManager toolBarManager = actionBars.getToolBarManager();
|
||||
toolBarManager.add(mAddAction);
|
||||
toolBarManager.add(mDeleteAction);
|
||||
toolBarManager.add(new Separator());
|
||||
toolBarManager.add(mUpAction);
|
||||
toolBarManager.add(mDownAction);
|
||||
|
||||
IMenuManager menuManager = actionBars.getMenuManager();
|
||||
menuManager.add(mAddAction);
|
||||
menuManager.add(mDeleteAction);
|
||||
menuManager.add(new Separator());
|
||||
menuManager.add(mUpAction);
|
||||
menuManager.add(mDownAction);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.ui.part.IPage#dispose()
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
breakConnectionWithEditor();
|
||||
|
||||
// dispose
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.ui.part.IPage#getControl()
|
||||
*/
|
||||
@Override
|
||||
public Control getControl() {
|
||||
return getViewer().getControl();
|
||||
}
|
||||
|
||||
void setNewEditor(GraphicalLayoutEditor editor) {
|
||||
mEditor = editor;
|
||||
setupOutline();
|
||||
}
|
||||
|
||||
void breakConnectionWithEditor() {
|
||||
// unhook outline viewer
|
||||
mEditor.getSelectionSynchronizer().removeViewer(getViewer());
|
||||
}
|
||||
|
||||
private void setupOutline() {
|
||||
getViewer().setEditDomain(mEditor.getEditDomain());
|
||||
|
||||
// hook outline viewer
|
||||
mEditor.getSelectionSynchronizer().addViewer(getViewer());
|
||||
|
||||
// initialize outline viewer with model
|
||||
getViewer().setContents(mEditor.getModel());
|
||||
}
|
||||
|
||||
private void setupContextMenu() {
|
||||
MenuManager menuManager = new MenuManager();
|
||||
menuManager.setRemoveAllWhenShown(true);
|
||||
menuManager.addMenuListener(new IMenuListener() {
|
||||
/**
|
||||
* The menu is about to be shown. The menu manager has already been
|
||||
* requested to remove any existing menu item. This method gets the
|
||||
* tree selection and if it is of the appropriate type it re-creates
|
||||
* the necessary actions.
|
||||
*/
|
||||
public void menuAboutToShow(IMenuManager manager) {
|
||||
UiElementNode selected = getModelSelection();
|
||||
|
||||
if (selected != null) {
|
||||
doCreateMenuAction(manager, selected);
|
||||
return;
|
||||
}
|
||||
doCreateMenuAction(manager, null /* ui_node */);
|
||||
}
|
||||
});
|
||||
Control control = getControl();
|
||||
Menu contextMenu = menuManager.createContextMenu(control);
|
||||
control.setMenu(contextMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the menu actions to the context menu when the given UI node is selected in
|
||||
* the tree view.
|
||||
*
|
||||
* @param manager The context menu manager
|
||||
* @param ui_node The UI node selected in the tree. Can be null, in which case the root
|
||||
* is to be modified.
|
||||
*/
|
||||
private void doCreateMenuAction(IMenuManager manager, UiElementNode ui_node) {
|
||||
if (ui_node != null && ui_node.getXmlNode() != null) {
|
||||
manager.add(new CopyCutAction(mEditor.getLayoutEditor(), mEditor.getClipboard(),
|
||||
null, ui_node, true /* cut */));
|
||||
manager.add(new CopyCutAction(mEditor.getLayoutEditor(), mEditor.getClipboard(),
|
||||
null, ui_node, false /* cut */));
|
||||
// Paste is not valid if it would add a second element on a terminal element
|
||||
// which parent is a document -- an XML document can only have one child. This
|
||||
// means paste is valid if the current UI node can have children or if the parent
|
||||
// is not a document.
|
||||
UiElementNode ui_root = ui_node.getUiRoot();
|
||||
if (ui_root.getDescriptor().hasChildren() ||
|
||||
!(ui_root.getUiParent() instanceof UiDocumentNode)) {
|
||||
manager.add(new PasteAction(mEditor.getLayoutEditor(), mEditor.getClipboard(),
|
||||
ui_node));
|
||||
}
|
||||
manager.add(new Separator());
|
||||
}
|
||||
|
||||
// Append "add" and "remove" actions. They do the same thing as the add/remove
|
||||
// buttons on the side.
|
||||
manager.add(mAddAction);
|
||||
manager.add(mDeleteAction);
|
||||
|
||||
manager.add(new Separator());
|
||||
|
||||
manager.add(mUpAction);
|
||||
manager.add(mDownAction);
|
||||
|
||||
manager.add(new Separator());
|
||||
|
||||
Action propertiesAction = new Action("Properties") {
|
||||
@Override
|
||||
public void run() {
|
||||
EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID,
|
||||
true /* activate */);
|
||||
}
|
||||
};
|
||||
propertiesAction.setToolTipText("Displays properties of the selected element.");
|
||||
manager.add(propertiesAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the outline view with the model of the {@link GraphicalLayoutEditor}.
|
||||
* <p/>
|
||||
* This attemps to preserve the selection, if any.
|
||||
*/
|
||||
public void reloadModel() {
|
||||
// Attemps to preserve the UiNode selection, if any
|
||||
UiElementNode uiNode = null;
|
||||
try {
|
||||
// get current selection using the model rather than the edit part as
|
||||
// reloading the content may change the actual edit part.
|
||||
uiNode = getModelSelection();
|
||||
|
||||
// perform the update
|
||||
getViewer().setContents(mEditor.getModel());
|
||||
|
||||
} finally {
|
||||
// restore selection
|
||||
setModelSelection(uiNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently selected element, if any, in the viewer.
|
||||
* This returns the viewer's elements (i.e. an {@link UiElementTreeEditPart})
|
||||
* and not the underlying model node.
|
||||
* <p/>
|
||||
* When there is no actual selection, this might still return the root node,
|
||||
* which is of type {@link UiDocumentTreeEditPart}.
|
||||
*/
|
||||
private UiElementTreeEditPart getViewerSelection() {
|
||||
ISelection selection = getSelection();
|
||||
if (selection instanceof StructuredSelection) {
|
||||
StructuredSelection structuredSelection = (StructuredSelection)selection;
|
||||
|
||||
if (structuredSelection.size() == 1) {
|
||||
Object selectedObj = structuredSelection.getFirstElement();
|
||||
|
||||
if (selectedObj instanceof UiElementTreeEditPart) {
|
||||
return (UiElementTreeEditPart) selectedObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently selected model element, which is either an
|
||||
* {@link UiViewTreeEditPart} or an {@link UiLayoutTreeEditPart}.
|
||||
* <p/>
|
||||
* Returns null if there is no selection or if the implicit root is "selected"
|
||||
* (which actually represents the lack of a real element selection.)
|
||||
*/
|
||||
private UiElementNode getModelSelection() {
|
||||
|
||||
UiElementTreeEditPart part = getViewerSelection();
|
||||
|
||||
if (part instanceof UiViewTreeEditPart || part instanceof UiLayoutTreeEditPart) {
|
||||
return (UiElementNode) part.getModel();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the corresponding edit part in the tree viewer.
|
||||
*/
|
||||
private void setViewerSelection(UiElementTreeEditPart selectedPart) {
|
||||
if (selectedPart != null && !(selectedPart instanceof UiDocumentTreeEditPart)) {
|
||||
LinkedList<UiElementTreeEditPart> segments = new LinkedList<UiElementTreeEditPart>();
|
||||
for (UiElementTreeEditPart part = selectedPart;
|
||||
!(part instanceof UiDocumentTreeEditPart);
|
||||
part = (UiElementTreeEditPart) part.getParent()) {
|
||||
segments.add(0, part);
|
||||
}
|
||||
setSelection(new TreeSelection(new TreePath(segments.toArray())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the corresponding model element in the tree viewer.
|
||||
*/
|
||||
private void setModelSelection(UiElementNode uiNodeToSelect) {
|
||||
if (uiNodeToSelect != null) {
|
||||
|
||||
// find an edit part that has the requested model element
|
||||
UiElementTreeEditPart part = findPartForModel(
|
||||
(UiElementTreeEditPart) getViewer().getContents(),
|
||||
uiNodeToSelect);
|
||||
|
||||
// if we found a part, select it and reveal it
|
||||
if (part != null) {
|
||||
setViewerSelection(part);
|
||||
getViewer().reveal(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that tries to find an edit part that matches a given model UI node.
|
||||
*
|
||||
* @param rootPart The root of the viewer edit parts
|
||||
* @param uiNode The UI node model to find
|
||||
* @return The part that matches the model or null if it's not in the sub tree.
|
||||
*/
|
||||
private UiElementTreeEditPart findPartForModel(UiElementTreeEditPart rootPart,
|
||||
UiElementNode uiNode) {
|
||||
if (rootPart.getModel() == uiNode) {
|
||||
return rootPart;
|
||||
}
|
||||
|
||||
for (Object part : rootPart.getChildren()) {
|
||||
if (part instanceof UiElementTreeEditPart) {
|
||||
UiElementTreeEditPart found = findPartForModel(
|
||||
(UiElementTreeEditPart) part, uiNode);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a custom tooltip when hovering over tree items.
|
||||
* <p/>
|
||||
* The tooltip will display the element's javadoc, if any, or the item's getText otherwise.
|
||||
*/
|
||||
private void setupTooltip() {
|
||||
final Tree tree = (Tree) getControl();
|
||||
|
||||
/*
|
||||
* Reference:
|
||||
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
|
||||
*/
|
||||
|
||||
final Listener listener = new Listener() {
|
||||
Shell tip = null;
|
||||
Label label = null;
|
||||
|
||||
public void handleEvent(Event event) {
|
||||
switch(event.type) {
|
||||
case SWT.Dispose:
|
||||
case SWT.KeyDown:
|
||||
case SWT.MouseExit:
|
||||
case SWT.MouseDown:
|
||||
case SWT.MouseMove:
|
||||
if (tip != null) {
|
||||
tip.dispose();
|
||||
tip = null;
|
||||
label = null;
|
||||
}
|
||||
break;
|
||||
case SWT.MouseHover:
|
||||
if (tip != null) {
|
||||
tip.dispose();
|
||||
tip = null;
|
||||
label = null;
|
||||
}
|
||||
|
||||
String tooltip = null;
|
||||
|
||||
TreeItem item = tree.getItem(new Point(event.x, event.y));
|
||||
if (item != null) {
|
||||
Object data = item.getData();
|
||||
if (data instanceof UiElementTreeEditPart) {
|
||||
Object model = ((UiElementTreeEditPart) data).getModel();
|
||||
if (model instanceof UiElementNode) {
|
||||
tooltip = ((UiElementNode) model).getDescriptor().getTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
if (tooltip == null) {
|
||||
tooltip = item.getText();
|
||||
} else {
|
||||
tooltip = item.getText() + ":\r" + tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (tooltip != null) {
|
||||
Shell shell = tree.getShell();
|
||||
Display display = tree.getDisplay();
|
||||
|
||||
tip = new Shell(shell, SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
|
||||
tip.setBackground(display .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
|
||||
FillLayout layout = new FillLayout();
|
||||
layout.marginWidth = 2;
|
||||
tip.setLayout(layout);
|
||||
label = new Label(tip, SWT.NONE);
|
||||
label.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
|
||||
label.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
|
||||
label.setData("_TABLEITEM", item);
|
||||
label.setText(tooltip);
|
||||
label.addListener(SWT.MouseExit, this);
|
||||
label.addListener(SWT.MouseDown, this);
|
||||
Point size = tip.computeSize(SWT.DEFAULT, SWT.DEFAULT);
|
||||
Rectangle rect = item.getBounds(0);
|
||||
Point pt = tree.toDisplay(rect.x, rect.y);
|
||||
tip.setBounds(pt.x, pt.y, size.x, size.y);
|
||||
tip.setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tree.addListener(SWT.Dispose, listener);
|
||||
tree.addListener(SWT.KeyDown, listener);
|
||||
tree.addListener(SWT.MouseMove, listener);
|
||||
tree.addListener(SWT.MouseHover, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up double-click action on the tree.
|
||||
* <p/>
|
||||
* By default, double-click (a.k.a. "default selection") on a valid list item will
|
||||
* show the property view.
|
||||
*/
|
||||
private void setupDoubleClick() {
|
||||
final Tree tree = (Tree) getControl();
|
||||
|
||||
tree.addListener(SWT.DefaultSelection, new Listener() {
|
||||
public void handleEvent(Event event) {
|
||||
EclipseUiHelper.showView(EclipseUiHelper.PROPERTY_SHEET_VIEW_ID,
|
||||
true /* activate */);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------
|
||||
|
||||
private class UiOutlineActions extends UiActions {
|
||||
|
||||
@Override
|
||||
protected UiDocumentNode getRootNode() {
|
||||
return mEditor.getModel(); // this is LayoutEditor.getUiRootNode()
|
||||
}
|
||||
|
||||
// Select the new item
|
||||
@Override
|
||||
protected void selectUiNode(UiElementNode uiNodeToSelect) {
|
||||
setModelSelection(uiNodeToSelect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commitPendingXmlChanges() {
|
||||
// Pass. There is nothing to commit before the XML is changed here.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,405 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.layoutlib.api.IXmlPullParser;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* XmlPullParser implementation on top of {@link UiElementNode}.
|
||||
* <p/>It's designed to work on layout files, and will most likely not work on other resource
|
||||
* files.
|
||||
*/
|
||||
public final class UiElementPullParser implements IXmlPullParser {
|
||||
|
||||
private final ArrayList<UiElementNode> mNodeStack = new ArrayList<UiElementNode>();
|
||||
private int mParsingState = START_DOCUMENT;
|
||||
private UiElementNode mRoot;
|
||||
|
||||
public UiElementPullParser(UiElementNode top) {
|
||||
mRoot = top;
|
||||
push(mRoot);
|
||||
}
|
||||
|
||||
private UiElementNode getCurrentNode() {
|
||||
if (mNodeStack.size() > 0) {
|
||||
return mNodeStack.get(mNodeStack.size()-1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Node getAttribute(int i) {
|
||||
if (mParsingState != START_TAG) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
// get the current uiNode
|
||||
UiElementNode uiNode = getCurrentNode();
|
||||
|
||||
// get its xml node
|
||||
Node xmlNode = uiNode.getXmlNode();
|
||||
|
||||
if (xmlNode != null) {
|
||||
return xmlNode.getAttributes().item(i);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void push(UiElementNode node) {
|
||||
mNodeStack.add(node);
|
||||
}
|
||||
|
||||
private UiElementNode pop() {
|
||||
return mNodeStack.remove(mNodeStack.size()-1);
|
||||
}
|
||||
|
||||
// ------------- IXmlPullParser --------
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation returns the underlying DOM node.
|
||||
*/
|
||||
public Object getViewKey() {
|
||||
return getCurrentNode();
|
||||
}
|
||||
|
||||
// ------------- XmlPullParser --------
|
||||
|
||||
public void setFeature(String name, boolean state) throws XmlPullParserException {
|
||||
if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
|
||||
return;
|
||||
}
|
||||
if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
|
||||
return;
|
||||
}
|
||||
throw new XmlPullParserException("Unsupported feature: " + name);
|
||||
}
|
||||
|
||||
public boolean getFeature(String name) {
|
||||
if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
|
||||
return true;
|
||||
}
|
||||
if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setProperty(String name, Object value) throws XmlPullParserException {
|
||||
throw new XmlPullParserException("setProperty() not supported");
|
||||
}
|
||||
|
||||
public Object getProperty(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setInput(Reader in) throws XmlPullParserException {
|
||||
throw new XmlPullParserException("setInput() not supported");
|
||||
}
|
||||
|
||||
public void setInput(InputStream inputStream, String inputEncoding)
|
||||
throws XmlPullParserException {
|
||||
throw new XmlPullParserException("setInput() not supported");
|
||||
}
|
||||
|
||||
public void defineEntityReplacementText(String entityName, String replacementText)
|
||||
throws XmlPullParserException {
|
||||
throw new XmlPullParserException("defineEntityReplacementText() not supported");
|
||||
}
|
||||
|
||||
public String getNamespacePrefix(int pos) throws XmlPullParserException {
|
||||
throw new XmlPullParserException("getNamespacePrefix() not supported");
|
||||
}
|
||||
|
||||
public String getInputEncoding() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getNamespace(String prefix) {
|
||||
throw new RuntimeException("getNamespace() not supported");
|
||||
}
|
||||
|
||||
public int getNamespaceCount(int depth) throws XmlPullParserException {
|
||||
throw new XmlPullParserException("getNamespaceCount() not supported");
|
||||
}
|
||||
|
||||
public String getPositionDescription() {
|
||||
return "XML DOM element depth:" + mNodeStack.size();
|
||||
}
|
||||
|
||||
public String getNamespaceUri(int pos) throws XmlPullParserException {
|
||||
throw new XmlPullParserException("getNamespaceUri() not supported");
|
||||
}
|
||||
|
||||
public int getColumnNumber() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getAttributeCount() {
|
||||
UiElementNode node = getCurrentNode();
|
||||
if (node != null) {
|
||||
return node.getUiAttributes().size();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getAttributeName(int i) {
|
||||
Node attribute = getAttribute(i);
|
||||
if (attribute != null) {
|
||||
return attribute.getLocalName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getAttributeNamespace(int i) {
|
||||
Node attribute = getAttribute(i);
|
||||
if (attribute != null) {
|
||||
return attribute.getNamespaceURI();
|
||||
}
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
public String getAttributePrefix(int i) {
|
||||
Node attribute = getAttribute(i);
|
||||
if (attribute != null) {
|
||||
return attribute.getPrefix();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getAttributeType(int arg0) {
|
||||
return "CDATA";
|
||||
}
|
||||
|
||||
public String getAttributeValue(int i) {
|
||||
Node attribute = getAttribute(i);
|
||||
if (attribute != null) {
|
||||
return attribute.getNodeValue();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getAttributeValue(String namespace, String localName) {
|
||||
// get the current uiNode
|
||||
UiElementNode uiNode = getCurrentNode();
|
||||
|
||||
// get its xml node
|
||||
Node xmlNode = uiNode.getXmlNode();
|
||||
|
||||
if (xmlNode != null) {
|
||||
Node attribute = xmlNode.getAttributes().getNamedItemNS(namespace, localName);
|
||||
if (attribute != null) {
|
||||
return attribute.getNodeValue();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getDepth() {
|
||||
return mNodeStack.size();
|
||||
}
|
||||
|
||||
public int getEventType() throws XmlPullParserException {
|
||||
return mParsingState;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
if (mParsingState == START_TAG || mParsingState == END_TAG) {
|
||||
return getCurrentNode().getDescriptor().getXmlLocalName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
if (mParsingState == START_TAG || mParsingState == END_TAG) {
|
||||
return getCurrentNode().getDescriptor().getNamespace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
if (mParsingState == START_TAG || mParsingState == END_TAG) {
|
||||
// FIXME will NEVER work
|
||||
if (getCurrentNode().getDescriptor().getXmlLocalName().startsWith("android:")) { //$NON-NLS-1$
|
||||
return "android"; //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public char[] getTextCharacters(int[] arg0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isAttributeDefault(int arg0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEmptyElementTag() throws XmlPullParserException {
|
||||
if (mParsingState == START_TAG) {
|
||||
return getCurrentNode().getUiChildren().size() == 0;
|
||||
}
|
||||
|
||||
throw new XmlPullParserException("Must be on START_TAG");
|
||||
}
|
||||
|
||||
public boolean isWhitespace() throws XmlPullParserException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int next() throws XmlPullParserException, IOException {
|
||||
UiElementNode node;
|
||||
switch (mParsingState) {
|
||||
case END_DOCUMENT:
|
||||
throw new XmlPullParserException("Nothing after the end");
|
||||
case START_DOCUMENT:
|
||||
/* intended fall-through */
|
||||
case START_TAG:
|
||||
// get the current node, and look for text or children (children first)
|
||||
node = getCurrentNode();
|
||||
List<UiElementNode> children = node.getUiChildren();
|
||||
if (children.size() > 0) {
|
||||
// move to the new child, and don't change the state.
|
||||
push(children.get(0));
|
||||
|
||||
// in case the current state is CURRENT_DOC, we set the proper state.
|
||||
mParsingState = START_TAG;
|
||||
} else {
|
||||
if (mParsingState == START_DOCUMENT) {
|
||||
// this handles the case where there's no node.
|
||||
mParsingState = END_DOCUMENT;
|
||||
} else {
|
||||
mParsingState = END_TAG;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case END_TAG:
|
||||
// look for a sibling. if no sibling, go back to the parent
|
||||
node = getCurrentNode();
|
||||
node = node.getUiNextSibling();
|
||||
if (node != null) {
|
||||
// to go to the sibling, we need to remove the current node,
|
||||
pop();
|
||||
// and add its sibling.
|
||||
push(node);
|
||||
mParsingState = START_TAG;
|
||||
} else {
|
||||
// move back to the parent
|
||||
pop();
|
||||
|
||||
// we have only one element left (mRoot), then we're done with the document.
|
||||
if (mNodeStack.size() == 1) {
|
||||
mParsingState = END_DOCUMENT;
|
||||
} else {
|
||||
mParsingState = END_TAG;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TEXT:
|
||||
// not used
|
||||
break;
|
||||
case CDSECT:
|
||||
// not used
|
||||
break;
|
||||
case ENTITY_REF:
|
||||
// not used
|
||||
break;
|
||||
case IGNORABLE_WHITESPACE:
|
||||
// not used
|
||||
break;
|
||||
case PROCESSING_INSTRUCTION:
|
||||
// not used
|
||||
break;
|
||||
case COMMENT:
|
||||
// not used
|
||||
break;
|
||||
case DOCDECL:
|
||||
// not used
|
||||
break;
|
||||
}
|
||||
|
||||
return mParsingState;
|
||||
}
|
||||
|
||||
public int nextTag() throws XmlPullParserException, IOException {
|
||||
int eventType = next();
|
||||
if (eventType != START_TAG && eventType != END_TAG) {
|
||||
throw new XmlPullParserException("expected start or end tag", this, null);
|
||||
}
|
||||
return eventType;
|
||||
}
|
||||
|
||||
public String nextText() throws XmlPullParserException, IOException {
|
||||
if (getEventType() != START_TAG) {
|
||||
throw new XmlPullParserException("parser must be on START_TAG to read next text", this,
|
||||
null);
|
||||
}
|
||||
int eventType = next();
|
||||
if (eventType == TEXT) {
|
||||
String result = getText();
|
||||
eventType = next();
|
||||
if (eventType != END_TAG) {
|
||||
throw new XmlPullParserException(
|
||||
"event TEXT it must be immediately followed by END_TAG", this, null);
|
||||
}
|
||||
return result;
|
||||
} else if (eventType == END_TAG) {
|
||||
return "";
|
||||
} else {
|
||||
throw new XmlPullParserException("parser must be on START_TAG or TEXT to read text",
|
||||
this, null);
|
||||
}
|
||||
}
|
||||
|
||||
public int nextToken() throws XmlPullParserException, IOException {
|
||||
return next();
|
||||
}
|
||||
|
||||
public void require(int type, String namespace, String name) throws XmlPullParserException,
|
||||
IOException {
|
||||
if (type != getEventType() || (namespace != null && !namespace.equals(getNamespace()))
|
||||
|| (name != null && !name.equals(getName())))
|
||||
throw new XmlPullParserException("expected " + TYPES[type] + getPositionDescription());
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.graphics.Point;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.layout.FillLayout;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Event;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
import org.eclipse.swt.widgets.Tree;
|
||||
import org.eclipse.swt.widgets.TreeItem;
|
||||
import org.eclipse.ui.views.properties.PropertySheetEntry;
|
||||
import org.eclipse.ui.views.properties.PropertySheetPage;
|
||||
|
||||
/**
|
||||
* A customized property sheet page for the graphical layout editor.
|
||||
* <p/>
|
||||
* Currently it just provides a custom tooltip to display attributes javadocs.
|
||||
*/
|
||||
public class UiPropertySheetPage extends PropertySheetPage {
|
||||
|
||||
|
||||
public UiPropertySheetPage() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createControl(Composite parent) {
|
||||
super.createControl(parent);
|
||||
|
||||
setupTooltip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a custom tooltip when hovering over tree items.
|
||||
* <p/>
|
||||
* The tooltip will display the element's javadoc, if any, or the item's getText otherwise.
|
||||
*/
|
||||
private void setupTooltip() {
|
||||
final Tree tree = (Tree) getControl();
|
||||
|
||||
/*
|
||||
* Reference:
|
||||
* http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
|
||||
*/
|
||||
|
||||
final Listener listener = new Listener() {
|
||||
Shell tip = null;
|
||||
Label label = null;
|
||||
|
||||
public void handleEvent(Event event) {
|
||||
switch(event.type) {
|
||||
case SWT.Dispose:
|
||||
case SWT.KeyDown:
|
||||
case SWT.MouseExit:
|
||||
case SWT.MouseDown:
|
||||
case SWT.MouseMove:
|
||||
if (tip != null) {
|
||||
tip.dispose();
|
||||
tip = null;
|
||||
label = null;
|
||||
}
|
||||
break;
|
||||
case SWT.MouseHover:
|
||||
if (tip != null) {
|
||||
tip.dispose();
|
||||
tip = null;
|
||||
label = null;
|
||||
}
|
||||
|
||||
String tooltip = null;
|
||||
|
||||
TreeItem item = tree.getItem(new Point(event.x, event.y));
|
||||
if (item != null) {
|
||||
Object data = item.getData();
|
||||
if (data instanceof PropertySheetEntry) {
|
||||
tooltip = ((PropertySheetEntry) data).getDescription();
|
||||
}
|
||||
|
||||
if (tooltip == null) {
|
||||
tooltip = item.getText();
|
||||
} else {
|
||||
tooltip = item.getText() + ":\r" + tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
if (tooltip != null) {
|
||||
Shell shell = tree.getShell();
|
||||
Display display = tree.getDisplay();
|
||||
|
||||
tip = new Shell(shell, SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL);
|
||||
tip.setBackground(display .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
|
||||
FillLayout layout = new FillLayout();
|
||||
layout.marginWidth = 2;
|
||||
tip.setLayout(layout);
|
||||
label = new Label(tip, SWT.NONE);
|
||||
label.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
|
||||
label.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
|
||||
label.setData("_TABLEITEM", item);
|
||||
label.setText(tooltip);
|
||||
label.addListener(SWT.MouseExit, this);
|
||||
label.addListener(SWT.MouseDown, this);
|
||||
Point size = tip.computeSize(SWT.DEFAULT, SWT.DEFAULT);
|
||||
Rectangle rect = item.getBounds(0);
|
||||
Point pt = tree.toDisplay(rect.x, rect.y);
|
||||
tip.setBounds(pt.x, pt.y, size.x, size.y);
|
||||
tip.setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tree.addListener(SWT.Dispose, listener);
|
||||
tree.addListener(SWT.KeyDown, listener);
|
||||
tree.addListener(SWT.MouseMove, listener);
|
||||
tree.addListener(SWT.MouseHover, listener);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.resources.ViewClassInfo;
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.IType;
|
||||
import org.eclipse.jdt.core.ITypeHierarchy;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Service responsible for creating/managing {@link ElementDescriptor} objects for custom
|
||||
* View classes per project.
|
||||
* <p/>
|
||||
* The service provides an on-demand monitoring of custom classes to check for changes. Monitoring
|
||||
* starts once a request for an {@link ElementDescriptor} object has been done for a specific
|
||||
* class.<br>
|
||||
* The monitoring will notify a listen of any changes in the class triggering a change in its
|
||||
* associated {@link ElementDescriptor} object.
|
||||
* <p/>
|
||||
* If the custom class does not exist, no monitoring is put in place to avoid having to listen
|
||||
* to all class changes in the projects.
|
||||
*
|
||||
*/
|
||||
public final class CustomViewDescriptorService {
|
||||
|
||||
private static CustomViewDescriptorService sThis = new CustomViewDescriptorService();
|
||||
|
||||
/**
|
||||
* Map where keys are the project, and values are another map containing all the known
|
||||
* custom View class for this project. The custom View class are stored in a map
|
||||
* where the keys are the fully qualified class name, and the values are their associated
|
||||
* {@link ElementDescriptor}.
|
||||
*/
|
||||
private HashMap<IProject, HashMap<String, ElementDescriptor>> mCustomDescriptorMap =
|
||||
new HashMap<IProject, HashMap<String, ElementDescriptor>>();
|
||||
|
||||
/**
|
||||
* TODO will be used to update the ElementDescriptor of the custom view when it
|
||||
* is modified (either the class itself or its attributes.xml)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private ICustomViewDescriptorListener mListener;
|
||||
|
||||
/**
|
||||
* Classes which implements this interface provide a method that deal with modifications
|
||||
* in custom View class triggering a change in its associated {@link ViewClassInfo} object.
|
||||
*/
|
||||
public interface ICustomViewDescriptorListener {
|
||||
/**
|
||||
* Sent when a custom View class has changed and its {@link ElementDescriptor} was modified.
|
||||
* @param project the project containing the class.
|
||||
* @param className the fully qualified class name.
|
||||
* @param descriptor the updated ElementDescriptor.
|
||||
*/
|
||||
public void updatedClassInfo(IProject project, String className, ElementDescriptor descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the singleton instance of {@link CustomViewDescriptorService}.
|
||||
*/
|
||||
public static CustomViewDescriptorService getInstance() {
|
||||
return sThis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener receiving custom View class modification notifications.
|
||||
* @param listener the listener to receive the notifications.
|
||||
*
|
||||
* TODO will be used to update the ElementDescriptor of the custom view when it
|
||||
* is modified (either the class itself or its attributes.xml)
|
||||
*/
|
||||
public void setListener(ICustomViewDescriptorListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ElementDescriptor} for a particular project/class.
|
||||
* <p/>
|
||||
* If it is the first time the <code>ElementDescriptor</code> is requested, the method
|
||||
* will check that the specified class is in fact a custom View class. Once this is
|
||||
* established, a monitoring for that particular class is initiated. Any change will
|
||||
* trigger a notification to the {@link ICustomViewDescriptorListener}.
|
||||
* @param project the project containing the class.
|
||||
* @param fqClassName the fully qualified name of the class.
|
||||
* @return a <code>ElementDescriptor</code> or <code>null</code> if the class was not
|
||||
* a custom View class.
|
||||
*/
|
||||
public ElementDescriptor getDescriptor(IProject project, String fqClassName) {
|
||||
// look in the map first
|
||||
synchronized (mCustomDescriptorMap) {
|
||||
HashMap<String, ElementDescriptor> map = mCustomDescriptorMap.get(project);
|
||||
|
||||
if (map != null) {
|
||||
ElementDescriptor descriptor = map.get(fqClassName);
|
||||
if (descriptor != null) {
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
// if we step here, it looks like we haven't created it yet.
|
||||
// First lets check this is in fact a valid type in the project
|
||||
|
||||
try {
|
||||
// We expect the project to be both opened and of java type (since it's an android
|
||||
// project), so we can create a IJavaProject object from our IProject.
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
|
||||
// replace $ by . in the class name
|
||||
String javaClassName = fqClassName.replaceAll("\\$", "\\."); //$NON-NLS-1$ //$NON-NLS-2$
|
||||
|
||||
// look for the IType object for this class
|
||||
IType type = javaProject.findType(javaClassName);
|
||||
if (type != null && type.exists()) {
|
||||
// the type exists. Let's get the parent class and its ViewClassInfo.
|
||||
|
||||
// get the type hierarchy
|
||||
ITypeHierarchy hierarchy = type.newSupertypeHierarchy(
|
||||
new NullProgressMonitor());
|
||||
|
||||
ElementDescriptor parentDescriptor = getDescriptor(
|
||||
hierarchy.getSuperclass(type), project, hierarchy);
|
||||
|
||||
if (parentDescriptor != null) {
|
||||
// we have a valid parent, lets create a new ElementDescriptor.
|
||||
|
||||
ViewElementDescriptor descriptor = new ViewElementDescriptor(fqClassName,
|
||||
fqClassName, // ui_name
|
||||
fqClassName, // canonical class name
|
||||
null, // tooltip
|
||||
null, // sdk_url
|
||||
getAttributeDescriptor(type, parentDescriptor),
|
||||
null, // layout attributes
|
||||
null, // children
|
||||
false /* mandatory */);
|
||||
|
||||
synchronized (mCustomDescriptorMap) {
|
||||
map = mCustomDescriptorMap.get(project);
|
||||
if (map == null) {
|
||||
map = new HashMap<String, ElementDescriptor>();
|
||||
mCustomDescriptorMap.put(project, map);
|
||||
}
|
||||
|
||||
map.put(fqClassName, descriptor);
|
||||
}
|
||||
|
||||
//TODO setup listener on this resource change.
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
} catch (JavaModelException e) {
|
||||
// there was an error accessing any of the IType, we'll just return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes (if needed) and returns the {@link ElementDescriptor} for the specified type.
|
||||
*
|
||||
* @param type
|
||||
* @param project
|
||||
* @param typeHierarchy
|
||||
* @return A ViewElementDescriptor
|
||||
*/
|
||||
private ViewElementDescriptor getDescriptor(IType type, IProject project,
|
||||
ITypeHierarchy typeHierarchy) {
|
||||
// check if the type is a built-in View class.
|
||||
List<ElementDescriptor> builtInList = LayoutDescriptors.getInstance().getViewDescriptors();
|
||||
|
||||
String canonicalName = type.getFullyQualifiedName();
|
||||
|
||||
for (ElementDescriptor desc : builtInList) {
|
||||
if (desc instanceof ViewElementDescriptor) {
|
||||
ViewElementDescriptor viewDescriptor = (ViewElementDescriptor)desc;
|
||||
if (canonicalName.equals(viewDescriptor.getCanonicalClassName())) {
|
||||
return viewDescriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// it's not a built-in class? Lets look if the superclass is built-in
|
||||
IType parentType = typeHierarchy.getSuperclass(type);
|
||||
if (parentType != null) {
|
||||
ViewElementDescriptor parentDescriptor = getDescriptor(parentType, project,
|
||||
typeHierarchy);
|
||||
|
||||
if (parentDescriptor != null) {
|
||||
// parent class is a valid View class with a descriptor, so we create one
|
||||
// for this class.
|
||||
ViewElementDescriptor descriptor = new ViewElementDescriptor(canonicalName,
|
||||
canonicalName, // ui_name
|
||||
canonicalName, // canonical name
|
||||
null, // tooltip
|
||||
null, // sdk_url
|
||||
getAttributeDescriptor(type, parentDescriptor),
|
||||
null, // layout attributes
|
||||
null, // children
|
||||
false /* mandatory */);
|
||||
|
||||
// add it to the map
|
||||
synchronized (mCustomDescriptorMap) {
|
||||
HashMap<String, ElementDescriptor> map = mCustomDescriptorMap.get(project);
|
||||
|
||||
if (map == null) {
|
||||
map = new HashMap<String, ElementDescriptor>();
|
||||
mCustomDescriptorMap.put(project, map);
|
||||
}
|
||||
|
||||
map.put(canonicalName, descriptor);
|
||||
|
||||
}
|
||||
|
||||
//TODO setup listener on this resource change.
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
// class is neither a built-in view class, nor extend one. return null.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of {@link AttributeDescriptor} for the specified {@link IType}.
|
||||
* <p/>
|
||||
* The array should contain the descriptor for this type and all its supertypes.
|
||||
* @param type the type for which the {@link AttributeDescriptor} are returned.
|
||||
* @param parentDescriptor the {@link ElementDescriptor} of the direct superclass.
|
||||
*/
|
||||
private AttributeDescriptor[] getAttributeDescriptor(IType type,
|
||||
ElementDescriptor parentDescriptor) {
|
||||
// TODO add the class attribute descriptors to the parent descriptors.
|
||||
return parentDescriptor.getAttributes();
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.resources.ViewClassInfo;
|
||||
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo;
|
||||
import com.android.ide.eclipse.common.resources.ViewClassInfo.LayoutParamsInfo;
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
|
||||
import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Complete description of the layout structure.
|
||||
*/
|
||||
public class LayoutDescriptors {
|
||||
|
||||
// Public attributes names, attributes descriptors and elements descriptors
|
||||
public static final String ID_ATTR = "id"; //$NON-NLS-1$
|
||||
|
||||
/** Singleton instance */
|
||||
private static LayoutDescriptors sThis;
|
||||
|
||||
/** The document descriptor. Contains all layouts and views linked together. */
|
||||
private DocumentDescriptor mDescriptor =
|
||||
new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$
|
||||
|
||||
/** The list of all known ViewLayout descriptors. */
|
||||
private ArrayList<ElementDescriptor> mLayoutDescriptors = new ArrayList<ElementDescriptor>();
|
||||
|
||||
/** The list of all known View (not ViewLayout) descriptors. */
|
||||
private ArrayList<ElementDescriptor> mViewDescriptors = new ArrayList<ElementDescriptor>();
|
||||
|
||||
/** Returns a singleton instance of the {@link LayoutDescriptors}. */
|
||||
public static synchronized LayoutDescriptors getInstance() {
|
||||
if (sThis == null) {
|
||||
sThis = new LayoutDescriptors();
|
||||
}
|
||||
return sThis;
|
||||
}
|
||||
|
||||
/** @return the document descriptor. Contains all layouts and views linked together. */
|
||||
public DocumentDescriptor getDescriptor() {
|
||||
return mDescriptor;
|
||||
}
|
||||
|
||||
/** @return The read-only list of all known ViewLayout descriptors. */
|
||||
public List<ElementDescriptor> getLayoutDescriptors() {
|
||||
return Collections.unmodifiableList(mLayoutDescriptors);
|
||||
}
|
||||
|
||||
/** @return The read-only list of all known View (not ViewLayout) descriptors. */
|
||||
public List<ElementDescriptor> getViewDescriptors() {
|
||||
return Collections.unmodifiableList(mViewDescriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the document descriptor.
|
||||
* <p/>
|
||||
* It first computes the new children of the descriptor and then update them
|
||||
* all at once.
|
||||
* <p/>
|
||||
* TODO: differentiate groups from views in the tree UI? => rely on icons
|
||||
* <p/>
|
||||
*
|
||||
* @param views The list of views in the framework.
|
||||
* @param layouts The list of layouts in the framework.
|
||||
*/
|
||||
public synchronized void updateDescriptors(ViewClassInfo[] views, ViewClassInfo[] layouts) {
|
||||
ArrayList<ElementDescriptor> newViews = new ArrayList<ElementDescriptor>();
|
||||
if (views != null) {
|
||||
for (ViewClassInfo info : views) {
|
||||
ElementDescriptor desc = convertView(info);
|
||||
newViews.add(desc);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<ElementDescriptor> newLayouts = new ArrayList<ElementDescriptor>();
|
||||
if (layouts != null) {
|
||||
for (ViewClassInfo info : layouts) {
|
||||
ElementDescriptor desc = convertView(info);
|
||||
newLayouts.add(desc);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<ElementDescriptor> newDescriptors = new ArrayList<ElementDescriptor>();
|
||||
newDescriptors.addAll(newLayouts);
|
||||
newDescriptors.addAll(newViews);
|
||||
ElementDescriptor[] newArray = newDescriptors.toArray(
|
||||
new ElementDescriptor[newDescriptors.size()]);
|
||||
|
||||
// Link all layouts to everything else here.. recursively
|
||||
for (ElementDescriptor layoutDesc : newLayouts) {
|
||||
layoutDesc.setChildren(newArray);
|
||||
}
|
||||
|
||||
mViewDescriptors = newViews;
|
||||
mLayoutDescriptors = newLayouts;
|
||||
mDescriptor.setChildren(newArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an element descriptor from a given {@link ViewClassInfo}.
|
||||
*/
|
||||
private ElementDescriptor convertView(ViewClassInfo info) {
|
||||
String xml_name = info.getShortClassName();
|
||||
String tooltip = info.getJavaDoc();
|
||||
|
||||
// Process all View attributes
|
||||
ArrayList<AttributeDescriptor> attributes = new ArrayList<AttributeDescriptor>();
|
||||
DescriptorsUtils.appendAttributes(attributes,
|
||||
null, // elementName
|
||||
AndroidConstants.NS_RESOURCES,
|
||||
info.getAttributes(),
|
||||
null, // requiredAttributes
|
||||
null /* overrides */);
|
||||
|
||||
for (ViewClassInfo link = info.getSuperClass();
|
||||
link != null;
|
||||
link = link.getSuperClass()) {
|
||||
AttributeInfo[] attrList = link.getAttributes();
|
||||
if (attrList.length > 0) {
|
||||
attributes.add(new SeparatorAttributeDescriptor(
|
||||
String.format("Attributes from %1$s", link.getShortClassName())));
|
||||
DescriptorsUtils.appendAttributes(attributes,
|
||||
null, // elementName
|
||||
AndroidConstants.NS_RESOURCES,
|
||||
attrList,
|
||||
null, // requiredAttributes
|
||||
null /* overrides */);
|
||||
}
|
||||
}
|
||||
|
||||
// Process all LayoutParams attributes
|
||||
ArrayList<AttributeDescriptor> layoutAttributes = new ArrayList<AttributeDescriptor>();
|
||||
LayoutParamsInfo layoutParams = info.getLayoutData();
|
||||
|
||||
for(; layoutParams != null; layoutParams = layoutParams.getSuperClass()) {
|
||||
boolean need_separator = true;
|
||||
for (AttributeInfo attr_info : layoutParams.getAttributes()) {
|
||||
if (DescriptorsUtils.containsAttribute(layoutAttributes,
|
||||
AndroidConstants.NS_RESOURCES, attr_info)) {
|
||||
continue;
|
||||
}
|
||||
if (need_separator) {
|
||||
String title;
|
||||
if (layoutParams.getShortClassName().equals(
|
||||
AndroidConstants.CLASS_LAYOUTPARAMS)) {
|
||||
title = String.format("Layout Attributes from %1$s",
|
||||
layoutParams.getViewLayoutClass().getShortClassName());
|
||||
} else {
|
||||
title = String.format("Layout Attributes from %1$s (%2$s)",
|
||||
layoutParams.getViewLayoutClass().getShortClassName(),
|
||||
layoutParams.getShortClassName());
|
||||
}
|
||||
layoutAttributes.add(new SeparatorAttributeDescriptor(title));
|
||||
need_separator = false;
|
||||
}
|
||||
DescriptorsUtils.appendAttribute(layoutAttributes,
|
||||
null, // elementName
|
||||
AndroidConstants.NS_RESOURCES,
|
||||
attr_info,
|
||||
false, // required
|
||||
null /* overrides */);
|
||||
}
|
||||
}
|
||||
|
||||
return new ViewElementDescriptor(xml_name,
|
||||
xml_name, // ui_name
|
||||
info.getCanonicalClassName(),
|
||||
tooltip,
|
||||
null, // sdk_url
|
||||
attributes.toArray(new AttributeDescriptor[attributes.size()]),
|
||||
layoutAttributes.toArray(new AttributeDescriptor[layoutAttributes.size()]),
|
||||
null, // children
|
||||
false /* mandatory */);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.layout.uimodel.UiViewElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
/**
|
||||
* {@link ViewElementDescriptor} describes the properties expected for a given XML element node
|
||||
* representing a class in an XML Layout file.
|
||||
*
|
||||
* @see ElementDescriptor
|
||||
*/
|
||||
public final class ViewElementDescriptor extends ElementDescriptor {
|
||||
|
||||
private String mCanonicalClassName;
|
||||
|
||||
/** The list of layout attributes. Can be empty but not null. */
|
||||
private AttributeDescriptor[] mLayoutAttributes;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ViewElementDescriptor} based on its XML name, UI name,
|
||||
* the canonical name of the class it represents, its tooltip, its SDK url, its attributes list,
|
||||
* its children list and its mandatory flag.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
* @param ui_name The XML element name for the user interface, typically capitalized.
|
||||
* @param canonicalClassName The canonical class name the {@link ViewElementDescriptor} is
|
||||
* representing.
|
||||
* @param tooltip An optional tooltip. Can be null or empty.
|
||||
* @param sdk_url An optional SKD URL. Can be null or empty.
|
||||
* @param attributes The list of allowed attributes. Can be null or empty.
|
||||
* @param layoutAttributes The list of layout attributes. Can be null or empty.
|
||||
* @param children The list of allowed children. Can be null or empty.
|
||||
* @param mandatory Whether this node must always exist (even for empty models). A mandatory
|
||||
* UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
|
||||
* UI node MUST have an XML node attached and it will cease to exist when the XML node
|
||||
* ceases to exist.
|
||||
*/
|
||||
public ViewElementDescriptor(String xml_name, String ui_name,
|
||||
String canonicalClassName,
|
||||
String tooltip, String sdk_url,
|
||||
AttributeDescriptor[] attributes, AttributeDescriptor[] layoutAttributes,
|
||||
ElementDescriptor[] children, boolean mandatory) {
|
||||
super(xml_name, ui_name, tooltip, sdk_url, attributes, children, mandatory);
|
||||
mCanonicalClassName = canonicalClassName;
|
||||
mLayoutAttributes = layoutAttributes != null ? layoutAttributes : new AttributeDescriptor[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ElementDescriptor} based on its XML name, the canonical
|
||||
* name of the class it represents, and its children list.
|
||||
* The UI name is build by capitalizing the XML name.
|
||||
* The UI nodes will be non-mandatory.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
* @param canonicalClassName The canonical class name the {@link ViewElementDescriptor} is
|
||||
* representing.
|
||||
* @param children The list of allowed children. Can be null or empty.
|
||||
* @param mandatory Whether this node must always exist (even for empty models). A mandatory
|
||||
* UI node is never deleted and it may lack an actual XML node attached. A non-mandatory
|
||||
* UI node MUST have an XML node attached and it will cease to exist when the XML node
|
||||
* ceases to exist.
|
||||
*/
|
||||
public ViewElementDescriptor(String xml_name, String canonicalClassName,
|
||||
ElementDescriptor[] children,
|
||||
boolean mandatory) {
|
||||
super(xml_name, children, mandatory);
|
||||
mCanonicalClassName = canonicalClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ElementDescriptor} based on its XML name and children list.
|
||||
* The UI name is build by capitalizing the XML name.
|
||||
* The UI nodes will be non-mandatory.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
* @param canonicalClassName The canonical class name the {@link ViewElementDescriptor} is
|
||||
* representing.
|
||||
* @param children The list of allowed children. Can be null or empty.
|
||||
*/
|
||||
public ViewElementDescriptor(String xml_name, String canonicalClassName,
|
||||
ElementDescriptor[] children) {
|
||||
super(xml_name, children);
|
||||
mCanonicalClassName = canonicalClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ElementDescriptor} based on its XML name and on the canonical
|
||||
* name of the class it represents.
|
||||
* The UI name is build by capitalizing the XML name.
|
||||
* The UI nodes will be non-mandatory.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
* @param canonicalClassName The canonical class name the {@link ViewElementDescriptor} is
|
||||
* representing.
|
||||
*/
|
||||
public ViewElementDescriptor(String xml_name, String canonicalClassName) {
|
||||
super(xml_name);
|
||||
mCanonicalClassName = canonicalClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the canonical name of the class represented by this element descriptor.
|
||||
*/
|
||||
public String getCanonicalClassName() {
|
||||
return mCanonicalClassName;
|
||||
}
|
||||
|
||||
/** Returns the list of layout attributes. Can be empty but not null. */
|
||||
public AttributeDescriptor[] getLayoutAttributes() {
|
||||
return mLayoutAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiViewElementNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiElementNode createUiNode() {
|
||||
return new UiViewElementNode(this);
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.parts;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.draw2d.AbstractBackground;
|
||||
import org.eclipse.draw2d.ColorConstants;
|
||||
import org.eclipse.draw2d.FreeformLayer;
|
||||
import org.eclipse.draw2d.FreeformLayout;
|
||||
import org.eclipse.draw2d.Graphics;
|
||||
import org.eclipse.draw2d.IFigure;
|
||||
import org.eclipse.draw2d.Label;
|
||||
import org.eclipse.draw2d.geometry.Insets;
|
||||
import org.eclipse.draw2d.geometry.Rectangle;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.swt.graphics.ImageData;
|
||||
import org.eclipse.swt.graphics.PaletteData;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferInt;
|
||||
|
||||
/**
|
||||
* Graphical edit part for the root document.
|
||||
* <p/>
|
||||
* It acts as a simple container.
|
||||
*/
|
||||
public class UiDocumentEditPart extends UiElementEditPart {
|
||||
|
||||
private Display mDisplay;
|
||||
private FreeformLayer mLayer;
|
||||
private ImageBackground mImage;
|
||||
private Label mChild = null;
|
||||
|
||||
final static class ImageBackground extends AbstractBackground {
|
||||
|
||||
private BufferedImage mBufferedImage;
|
||||
private Image mImage;
|
||||
|
||||
ImageBackground() {
|
||||
}
|
||||
|
||||
ImageBackground(BufferedImage image, Display display) {
|
||||
setImage(image, display);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBackground(IFigure figure, Graphics graphics, Insets insets) {
|
||||
if (mImage != null) {
|
||||
Rectangle rect = getPaintRectangle(figure, insets);
|
||||
graphics.drawImage(mImage, rect.x, rect.y);
|
||||
}
|
||||
}
|
||||
|
||||
void setImage(BufferedImage image, Display display) {
|
||||
if (image != null) {
|
||||
int[] data = ((DataBufferInt)image.getData().getDataBuffer()).getData();
|
||||
|
||||
ImageData imageData = new ImageData(image.getWidth(), image.getHeight(), 32,
|
||||
new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF));
|
||||
|
||||
imageData.setPixels(0, 0, data.length, data, 0);
|
||||
|
||||
mImage = new Image(display, imageData);
|
||||
} else {
|
||||
mImage = null;
|
||||
}
|
||||
}
|
||||
|
||||
BufferedImage getBufferedImage() {
|
||||
return mBufferedImage;
|
||||
}
|
||||
}
|
||||
|
||||
public UiDocumentEditPart(UiDocumentNode uiDocumentNode, Display display) {
|
||||
super(uiDocumentNode);
|
||||
mDisplay = display;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IFigure createFigure() {
|
||||
mLayer = new FreeformLayer();
|
||||
mLayer.setLayoutManager(new FreeformLayout());
|
||||
|
||||
mLayer.setOpaque(true);
|
||||
mLayer.setBackgroundColor(ColorConstants.lightGray);
|
||||
|
||||
return mLayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshVisuals() {
|
||||
UiElementNode model = (UiElementNode)getModel();
|
||||
|
||||
Object editData = model.getEditData();
|
||||
if (editData instanceof BufferedImage) {
|
||||
BufferedImage image = (BufferedImage)editData;
|
||||
|
||||
if (mImage == null || image != mImage.getBufferedImage()) {
|
||||
mImage = new ImageBackground(image, mDisplay);
|
||||
}
|
||||
|
||||
mLayer.setBorder(mImage);
|
||||
|
||||
if (mChild != null && mChild.getParent() == mLayer) {
|
||||
mLayer.remove(mChild);
|
||||
}
|
||||
} else if (editData instanceof String) {
|
||||
mLayer.setBorder(null);
|
||||
if (mChild == null) {
|
||||
mChild = new Label();
|
||||
}
|
||||
mChild.setText((String)editData);
|
||||
|
||||
if (mChild != null && mChild.getParent() != mLayer) {
|
||||
mLayer.add(mChild);
|
||||
}
|
||||
Rectangle bounds = mChild.getTextBounds();
|
||||
bounds.x = bounds.y = 0;
|
||||
mLayer.setConstraint(mChild, bounds);
|
||||
}
|
||||
|
||||
// refresh the children as well
|
||||
refreshChildrenVisuals();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void hideSelection() {
|
||||
// no selection at this level.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showSelection() {
|
||||
// no selection at this level.
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.parts;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of {@link UiElementTreeEditPart} for the document root.
|
||||
*/
|
||||
public class UiDocumentTreeEditPart extends UiElementTreeEditPart {
|
||||
|
||||
public UiDocumentTreeEditPart(UiDocumentNode model) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected List getModelChildren() {
|
||||
return getUiNode().getUiChildren();
|
||||
}
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.parts;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.draw2d.geometry.Rectangle;
|
||||
import org.eclipse.gef.DragTracker;
|
||||
import org.eclipse.gef.EditPart;
|
||||
import org.eclipse.gef.EditPolicy;
|
||||
import org.eclipse.gef.GraphicalEditPart;
|
||||
import org.eclipse.gef.Request;
|
||||
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
|
||||
import org.eclipse.gef.editpolicies.SelectionEditPolicy;
|
||||
import org.eclipse.gef.tools.SelectEditPartTracker;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An {@link EditPart} for a {@link UiElementNode}.
|
||||
*/
|
||||
public abstract class UiElementEditPart extends AbstractGraphicalEditPart
|
||||
implements IUiUpdateListener {
|
||||
|
||||
public UiElementEditPart(UiElementNode uiElementNode) {
|
||||
setModel(uiElementNode);
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
// Derived classes must define these
|
||||
|
||||
abstract protected void hideSelection();
|
||||
abstract protected void showSelection();
|
||||
|
||||
//-------------------------
|
||||
// Base class overrides
|
||||
|
||||
@Override
|
||||
public DragTracker getDragTracker(Request request) {
|
||||
return new SelectEditPartTracker(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createEditPolicies() {
|
||||
installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, new SelectionEditPolicy() {
|
||||
@Override
|
||||
protected void hideSelection() {
|
||||
UiElementEditPart.this.hideSelection();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showSelection() {
|
||||
UiElementEditPart.this.showSelection();
|
||||
}
|
||||
});
|
||||
// TODO add editing policies
|
||||
}
|
||||
|
||||
/* (non-javadoc)
|
||||
* Returns a List containing the children model objects.
|
||||
* Must not return null, instead use the super which returns an empty list.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected List getModelChildren() {
|
||||
return getUiNode().getUiChildren();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
super.activate();
|
||||
getUiNode().addUpdateListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
super.deactivate();
|
||||
getUiNode().removeUpdateListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshVisuals() {
|
||||
if (getFigure().getParent() != null) {
|
||||
((GraphicalEditPart) getParent()).setLayoutConstraint(this, getFigure(), getBounds());
|
||||
}
|
||||
|
||||
// update the visuals of the children as well
|
||||
refreshChildrenVisuals();
|
||||
}
|
||||
|
||||
protected void refreshChildrenVisuals() {
|
||||
if (children != null) {
|
||||
for (Object child : children) {
|
||||
if (child instanceof UiElementEditPart) {
|
||||
UiElementEditPart childPart = (UiElementEditPart)child;
|
||||
childPart.refreshVisuals();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
// IUiUpdateListener implementation
|
||||
|
||||
public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
|
||||
// TODO: optimize by refreshing only when needed
|
||||
switch(state) {
|
||||
case ATTR_UPDATED:
|
||||
refreshVisuals();
|
||||
break;
|
||||
case CHILDREN_CHANGED:
|
||||
refreshChildren();
|
||||
|
||||
// new children list, need to update the layout
|
||||
refreshVisuals();
|
||||
break;
|
||||
case CREATED:
|
||||
refreshVisuals();
|
||||
break;
|
||||
case DELETED:
|
||||
// pass
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
// Local methods
|
||||
|
||||
/** @return The object model casted to an {@link UiElementNode} */
|
||||
protected final UiElementNode getUiNode() {
|
||||
return (UiElementNode) getModel();
|
||||
}
|
||||
|
||||
protected final ElementDescriptor getDescriptor() {
|
||||
return getUiNode().getDescriptor();
|
||||
}
|
||||
|
||||
protected final UiElementEditPart getEditPartParent() {
|
||||
EditPart parent = getParent();
|
||||
if (parent instanceof UiElementEditPart) {
|
||||
return (UiElementEditPart)parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a given XML attribute.
|
||||
* @param attrbName The local name of the attribute.
|
||||
* @return the attribute as a {@link String}, if it exists, or <code>null</code>
|
||||
*/
|
||||
protected final String getStringAttr(String attrName) {
|
||||
UiElementNode uiNode = getUiNode();
|
||||
if (uiNode.getXmlNode() != null) {
|
||||
Node xmlNode = uiNode.getXmlNode();
|
||||
if (xmlNode != null) {
|
||||
NamedNodeMap nodeAttributes = xmlNode.getAttributes();
|
||||
if (nodeAttributes != null) {
|
||||
Node attr = nodeAttributes.getNamedItemNS(
|
||||
AndroidConstants.NS_RESOURCES, attrName);
|
||||
if (attr != null) {
|
||||
return attr.getNodeValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected final Rectangle getBounds() {
|
||||
UiElementNode model = (UiElementNode)getModel();
|
||||
|
||||
Object editData = model.getEditData();
|
||||
if (editData instanceof Rectangle) {
|
||||
return (Rectangle)editData; // return a copy?
|
||||
}
|
||||
|
||||
// return a dummy rect
|
||||
return new Rectangle(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.parts;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.gef.editparts.AbstractTreeEditPart;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
|
||||
|
||||
/**
|
||||
* Base {@link AbstractTreeEditPart} to represent {@link UiElementNode} objects in the
|
||||
* {@link IContentOutlinePage} linked to the layout editor.
|
||||
*/
|
||||
public class UiElementTreeEditPart extends AbstractTreeEditPart {
|
||||
|
||||
public UiElementTreeEditPart(UiElementNode uiElementNode) {
|
||||
setModel(uiElementNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createEditPolicies() {
|
||||
// TODO Auto-generated method stub
|
||||
super.createEditPolicies();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Image getImage() {
|
||||
return getUiNode().getDescriptor().getIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getText() {
|
||||
return getUiNode().getShortDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
if (!isActive()) {
|
||||
super.activate();
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
if (isActive()) {
|
||||
super.deactivate();
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the casted model object represented by this {@link AbstractTreeEditPart}.
|
||||
*/
|
||||
protected UiElementNode getUiNode() {
|
||||
return (UiElementNode)getModel();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.parts;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.gef.EditPart;
|
||||
import org.eclipse.gef.EditPartFactory;
|
||||
import org.eclipse.gef.editparts.AbstractTreeEditPart;
|
||||
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
|
||||
|
||||
/**
|
||||
* {@link EditPartFactory} to create {@link AbstractTreeEditPart} for {@link UiElementNode} objects.
|
||||
* These objects are used in the {@link IContentOutlinePage} linked to the layout editor.
|
||||
*/
|
||||
public class UiElementTreeEditPartFactory implements EditPartFactory {
|
||||
|
||||
public EditPart createEditPart(EditPart context, Object model) {
|
||||
if (model instanceof UiDocumentNode) {
|
||||
return new UiDocumentTreeEditPart((UiDocumentNode) model);
|
||||
} else if (model instanceof UiElementNode) {
|
||||
UiElementNode node = (UiElementNode) model;
|
||||
if (node.getDescriptor().hasChildren()) {
|
||||
return new UiLayoutTreeEditPart(node);
|
||||
} else {
|
||||
return new UiViewTreeEditPart(node);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.parts;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.gef.EditPart;
|
||||
import org.eclipse.gef.EditPartFactory;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
|
||||
/**
|
||||
* A factory that returns the appropriate {@link EditPart} for a given model object.
|
||||
* <p/>
|
||||
* The only model objects we use are {@link UiElementNode} objects and they are
|
||||
* edited using {@link UiElementEditPart}.
|
||||
*/
|
||||
public class UiElementsEditPartFactory implements EditPartFactory {
|
||||
|
||||
private Display mDisplay;
|
||||
|
||||
public UiElementsEditPartFactory(Display display) {
|
||||
mDisplay = display;
|
||||
}
|
||||
|
||||
public EditPart createEditPart(EditPart context, Object model) {
|
||||
if (model instanceof UiDocumentNode) {
|
||||
return new UiDocumentEditPart((UiDocumentNode) model, mDisplay);
|
||||
} else if (model instanceof UiElementNode) {
|
||||
UiElementNode node = (UiElementNode) model;
|
||||
|
||||
if (node.getDescriptor().hasChildren()) {
|
||||
return new UiLayoutEditPart(node);
|
||||
}
|
||||
|
||||
return new UiViewEditPart(node);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.parts;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.draw2d.ColorConstants;
|
||||
import org.eclipse.draw2d.IFigure;
|
||||
import org.eclipse.draw2d.Label;
|
||||
import org.eclipse.draw2d.LineBorder;
|
||||
import org.eclipse.draw2d.XYLayout;
|
||||
import org.eclipse.gef.EditPolicy;
|
||||
import org.eclipse.gef.commands.Command;
|
||||
import org.eclipse.gef.editpolicies.ContainerEditPolicy;
|
||||
import org.eclipse.gef.requests.CreateRequest;
|
||||
|
||||
/**
|
||||
* Graphical edit part for an {@link UiElementNode} that represents a ViewLayout.
|
||||
* <p/>
|
||||
* It acts as a simple container.
|
||||
*/
|
||||
public final class UiLayoutEditPart extends UiElementEditPart {
|
||||
|
||||
public UiLayoutEditPart(UiElementNode uiElementNode) {
|
||||
super(uiElementNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createEditPolicies() {
|
||||
super.createEditPolicies();
|
||||
|
||||
installEditPolicy(EditPolicy.CONTAINER_ROLE, new ContainerEditPolicy() {
|
||||
@Override
|
||||
protected Command getCreateCommand(CreateRequest request) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IFigure createFigure() {
|
||||
Label f = new Label();
|
||||
f.setLayoutManager(new XYLayout());
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void hideSelection() {
|
||||
IFigure f = getFigure();
|
||||
if (f instanceof Label) {
|
||||
f.setBorder(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showSelection() {
|
||||
IFigure f = getFigure();
|
||||
if (f instanceof Label) {
|
||||
f.setBorder(new LineBorder(ColorConstants.red, 1));
|
||||
}
|
||||
}
|
||||
|
||||
public void showDropTarget() {
|
||||
IFigure f = getFigure();
|
||||
if (f instanceof Label) {
|
||||
f.setBorder(new LineBorder(ColorConstants.blue, 1));
|
||||
}
|
||||
}
|
||||
|
||||
public void hideDropTarget() {
|
||||
hideSelection();
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.parts;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of {@link UiElementTreeEditPart} for layout objects.
|
||||
*/
|
||||
public class UiLayoutTreeEditPart extends UiElementTreeEditPart {
|
||||
|
||||
public UiLayoutTreeEditPart(UiElementNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected List getModelChildren() {
|
||||
return getUiNode().getUiChildren();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.parts;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.draw2d.ColorConstants;
|
||||
import org.eclipse.draw2d.IFigure;
|
||||
import org.eclipse.draw2d.Label;
|
||||
import org.eclipse.draw2d.LineBorder;
|
||||
|
||||
/**
|
||||
* Graphical edit part for an {@link UiElementNode} that represents a View.
|
||||
*/
|
||||
public class UiViewEditPart extends UiElementEditPart {
|
||||
|
||||
public UiViewEditPart(UiElementNode uiElementNode) {
|
||||
super(uiElementNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IFigure createFigure() {
|
||||
Label f = new Label();
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void hideSelection() {
|
||||
IFigure f = getFigure();
|
||||
if (f instanceof Label) {
|
||||
f.setBorder(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showSelection() {
|
||||
IFigure f = getFigure();
|
||||
if (f instanceof Label) {
|
||||
f.setBorder(new LineBorder(ColorConstants.red, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.parts;
|
||||
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
/**
|
||||
* Implementation of {@link UiElementTreeEditPart} for view objects.
|
||||
*/
|
||||
public class UiViewTreeEditPart extends UiElementTreeEditPart {
|
||||
|
||||
public UiViewTreeEditPart(UiElementNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.editors.layout.uimodel;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.layout.descriptors.LayoutDescriptors;
|
||||
import com.android.ide.eclipse.editors.layout.descriptors.ViewElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
/**
|
||||
* Specialized version of {@link UiElementNode} for the {@link ViewElementDescriptor}s.
|
||||
*/
|
||||
public class UiViewElementNode extends UiElementNode {
|
||||
|
||||
private AttributeDescriptor[] mCachedAttributeDescriptors;
|
||||
|
||||
public UiViewElementNode(ViewElementDescriptor elementDescriptor) {
|
||||
super(elementDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an AttributeDescriptor array that depends on the current UiParent.
|
||||
* <p/>
|
||||
* The array merges both "direct" attributes with the descriptor layout attributes.
|
||||
* The array instance is cached and cleared if the UiParent is changed.
|
||||
*/
|
||||
@Override
|
||||
public AttributeDescriptor[] getAttributeDescriptors() {
|
||||
if (mCachedAttributeDescriptors != null) {
|
||||
return mCachedAttributeDescriptors;
|
||||
}
|
||||
|
||||
UiElementNode ui_parent = getUiParent();
|
||||
AttributeDescriptor[] direct_attrs = super.getAttributeDescriptors();
|
||||
mCachedAttributeDescriptors = direct_attrs;
|
||||
|
||||
AttributeDescriptor[] layout_attrs = null;
|
||||
boolean need_xmlns = false;
|
||||
|
||||
if (ui_parent instanceof UiDocumentNode) {
|
||||
// Limitation: right now the layout behaves as if everything was
|
||||
// owned by a FrameLayout.
|
||||
// TODO replace by something user-configurable.
|
||||
for (ElementDescriptor desc : LayoutDescriptors.getInstance().getLayoutDescriptors()) {
|
||||
if (desc instanceof ViewElementDescriptor &&
|
||||
desc.getXmlName().equals(AndroidConstants.CLASS_FRAMELAYOUT)) {
|
||||
layout_attrs = ((ViewElementDescriptor) desc).getLayoutAttributes();
|
||||
need_xmlns = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (ui_parent instanceof UiViewElementNode){
|
||||
layout_attrs =
|
||||
((ViewElementDescriptor) ui_parent.getDescriptor()).getLayoutAttributes();
|
||||
}
|
||||
|
||||
if (layout_attrs == null || layout_attrs.length == 0) {
|
||||
return mCachedAttributeDescriptors;
|
||||
}
|
||||
|
||||
mCachedAttributeDescriptors =
|
||||
new AttributeDescriptor[direct_attrs.length +
|
||||
layout_attrs.length +
|
||||
(need_xmlns ? 1 : 0)];
|
||||
System.arraycopy(direct_attrs, 0,
|
||||
mCachedAttributeDescriptors, 0,
|
||||
direct_attrs.length);
|
||||
System.arraycopy(layout_attrs, 0,
|
||||
mCachedAttributeDescriptors, direct_attrs.length,
|
||||
layout_attrs.length);
|
||||
if (need_xmlns) {
|
||||
AttributeDescriptor desc = new XmlnsAttributeDescriptor(
|
||||
"android", //$NON-NLS-1$
|
||||
AndroidConstants.NS_RESOURCES);
|
||||
mCachedAttributeDescriptors[direct_attrs.length + layout_attrs.length] = desc;
|
||||
}
|
||||
|
||||
return mCachedAttributeDescriptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent of this UI node.
|
||||
* <p/>
|
||||
* Also removes the cached AttributeDescriptor array that depends on the current UiParent.
|
||||
*/
|
||||
@Override
|
||||
protected void setUiParent(UiElementNode parent) {
|
||||
super.setUiParent(parent);
|
||||
mCachedAttributeDescriptors = null;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest;
|
||||
|
||||
import com.android.ide.eclipse.editors.AndroidContentAssist;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
|
||||
/**
|
||||
* Content Assist Processor for AndroidManifest.xml
|
||||
*/
|
||||
final class ManifestContentAssist extends AndroidContentAssist {
|
||||
|
||||
/**
|
||||
* Constructor for ManifestContentAssist
|
||||
*/
|
||||
public ManifestContentAssist() {
|
||||
super(new ElementDescriptor[] { AndroidManifestDescriptors.MANIFEST_ELEMENT });
|
||||
}
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.AndroidXPathFactory;
|
||||
import com.android.ide.eclipse.editors.AndroidEditor;
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.manifest.pages.ApplicationPage;
|
||||
import com.android.ide.eclipse.editors.manifest.pages.InstrumentationPage;
|
||||
import com.android.ide.eclipse.editors.manifest.pages.OverviewPage;
|
||||
import com.android.ide.eclipse.editors.manifest.pages.PermissionPage;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
|
||||
import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IMarker;
|
||||
import org.eclipse.core.resources.IMarkerDelta;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.ui.IEditorInput;
|
||||
import org.eclipse.ui.IEditorPart;
|
||||
import org.eclipse.ui.PartInitException;
|
||||
import org.eclipse.ui.part.FileEditorInput;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
|
||||
/**
|
||||
* Multi-page form editor for AndroidManifest.xml.
|
||||
*/
|
||||
public final class ManifestEditor extends AndroidEditor {
|
||||
private final static String EMPTY = ""; //$NON-NLS-1$
|
||||
|
||||
|
||||
/** Root node of the UI element hierarchy */
|
||||
private UiElementNode mUiManifestNode;
|
||||
/** Listener to update the root node if the resource framework changes */
|
||||
private Runnable mResourceRefreshListener;
|
||||
/** The Application Page tab */
|
||||
private ApplicationPage mAppPage;
|
||||
/** The Overview Manifest Page tab */
|
||||
private OverviewPage mOverviewPage;
|
||||
|
||||
/**
|
||||
* Creates the form editor for AndroidManifest.xml.
|
||||
*/
|
||||
public ManifestEditor() {
|
||||
super();
|
||||
initUiManifestNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the root node of the UI element hierarchy, which here
|
||||
* is the "manifest" node.
|
||||
*/
|
||||
@Override
|
||||
public UiElementNode getUiRootNode() {
|
||||
return mUiManifestNode;
|
||||
}
|
||||
|
||||
// ---- Base Class Overrides ----
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (mResourceRefreshListener != null) {
|
||||
EditorsPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener);
|
||||
mResourceRefreshListener = null;
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the various form pages.
|
||||
*/
|
||||
@Override
|
||||
protected void createFormPages() {
|
||||
try {
|
||||
addPage(mOverviewPage = new OverviewPage(this));
|
||||
addPage(mAppPage = new ApplicationPage(this));
|
||||
addPage(new PermissionPage(this));
|
||||
addPage(new InstrumentationPage(this));
|
||||
} catch (PartInitException e) {
|
||||
EditorsPlugin.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);
|
||||
IFile inputFile = getInputFile();
|
||||
if (inputFile != null) {
|
||||
setPartName(String.format("%1$s Manifest", inputFile.getProject().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the new XML Model, which XML root node is given.
|
||||
*
|
||||
* @param xml_doc The XML document, if available, or null if none exists.
|
||||
*/
|
||||
@Override
|
||||
protected void xmlModelChanged(Document xml_doc) {
|
||||
mUiManifestNode.setXmlDocument(xml_doc);
|
||||
if (xml_doc != null) {
|
||||
ElementDescriptor manifest_desc = mUiManifestNode.getDescriptor();
|
||||
try {
|
||||
XPath xpath = AndroidXPathFactory.newXPath();
|
||||
Node node = (Node) xpath.evaluate("/" + manifest_desc.getXmlName(), //$NON-NLS-1$
|
||||
xml_doc,
|
||||
XPathConstants.NODE);
|
||||
assert node != null && node.getNodeName().equals(manifest_desc.getXmlName());
|
||||
|
||||
// Refresh the manifest UI node and all its descendants
|
||||
mUiManifestNode.loadFromXmlNode(node);
|
||||
|
||||
startMonitoringMarkers();
|
||||
} catch (XPathExpressionException e) {
|
||||
EditorsPlugin.log(e, "XPath error when trying to find '%s' element in XML.", //$NON-NLS-1$
|
||||
manifest_desc.getXmlName());
|
||||
}
|
||||
}
|
||||
|
||||
super.xmlModelChanged(xml_doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and processes the current markers and adds a listener for marker changes.
|
||||
*/
|
||||
private void startMonitoringMarkers() {
|
||||
final IFile inputFile = getInputFile();
|
||||
if (inputFile != null) {
|
||||
updateFromExistingMarkers(inputFile);
|
||||
|
||||
ResourceMonitor.getMonitor().addFileListener(new IFileListener() {
|
||||
public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind) {
|
||||
if (file.equals(inputFile)) {
|
||||
processMarkerChanges(markerDeltas);
|
||||
}
|
||||
}
|
||||
}, IResourceDelta.CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the markers of the specified {@link IFile} and updates the error status of
|
||||
* {@link UiElementNode}s and {@link UiAttributeNode}s.
|
||||
* @param inputFile the file being edited.
|
||||
*/
|
||||
private void updateFromExistingMarkers(IFile inputFile) {
|
||||
try {
|
||||
// get the markers for the file
|
||||
IMarker[] markers = inputFile.findMarkers(AndroidConstants.MARKER_ANDROID, true,
|
||||
IResource.DEPTH_ZERO);
|
||||
|
||||
UiElementNode app_ui_node = mUiManifestNode.findUiChildNode(
|
||||
AndroidManifestDescriptors.APPLICATION_ELEMENT.getXmlName());
|
||||
List<UiElementNode> children = app_ui_node.getUiChildren();
|
||||
|
||||
for (IMarker marker : markers) {
|
||||
processMarker(marker, children, IResourceDelta.ADDED);
|
||||
}
|
||||
} catch (CoreException e) {
|
||||
// findMarkers can throw an exception, in which case, we'll do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a {@link IMarker} change.
|
||||
* @param markerDeltas the list of {@link IMarkerDelta}
|
||||
*/
|
||||
private void processMarkerChanges(IMarkerDelta[] markerDeltas) {
|
||||
UiElementNode app_ui_node = mUiManifestNode.findUiChildNode(
|
||||
AndroidManifestDescriptors.APPLICATION_ELEMENT.getXmlName());
|
||||
List<UiElementNode> children = app_ui_node.getUiChildren();
|
||||
|
||||
for (IMarkerDelta markerDelta : markerDeltas) {
|
||||
processMarker(markerDelta.getMarker(), children, markerDelta.getKind());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a new/old/updated marker.
|
||||
* @param marker The marker being added/removed/changed
|
||||
* @param nodeList the list of activity/service/provider/receiver nodes.
|
||||
* @param kind the change kind. Can be {@link IResourceDelta#ADDED},
|
||||
* {@link IResourceDelta#REMOVED}, or {@link IResourceDelta#CHANGED}
|
||||
*/
|
||||
private void processMarker(IMarker marker, List<UiElementNode> nodeList, int kind) {
|
||||
// get the data from the marker
|
||||
String nodeType = marker.getAttribute(AndroidConstants.MARKER_ATTR_TYPE, EMPTY);
|
||||
if (nodeType == EMPTY) {
|
||||
return;
|
||||
}
|
||||
|
||||
String className = marker.getAttribute(AndroidConstants.MARKER_ATTR_CLASS, EMPTY);
|
||||
if (className == EMPTY) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (UiElementNode ui_node : nodeList) {
|
||||
if (ui_node.getDescriptor().getXmlName().equals(nodeType)) {
|
||||
for (UiAttributeNode attr : ui_node.getUiAttributes()) {
|
||||
if (attr.getDescriptor().getXmlLocalName().equals(
|
||||
AndroidManifestDescriptors.ANDROID_NAME_ATTR)) {
|
||||
if (attr.getCurrentValue().equals(className)) {
|
||||
if (kind == IResourceDelta.REMOVED) {
|
||||
attr.setHasError(false);
|
||||
} else {
|
||||
attr.setHasError(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the initial UI Root Node, including the known mandatory elements.
|
||||
*/
|
||||
private void initUiManifestNode() {
|
||||
// The manifest UI node is always created, even if there's no corresponding XML node.
|
||||
if (mUiManifestNode == null) {
|
||||
ElementDescriptor manifest_desc = AndroidManifestDescriptors.MANIFEST_ELEMENT;
|
||||
mUiManifestNode = manifest_desc.createUiNode();
|
||||
mUiManifestNode.setEditor(this);
|
||||
|
||||
// Similarly, always create the /manifest/application and /manifest/uses-sdk nodes
|
||||
ElementDescriptor app_desc = AndroidManifestDescriptors.APPLICATION_ELEMENT;
|
||||
boolean present = false;
|
||||
for (UiElementNode ui_node : mUiManifestNode.getUiChildren()) {
|
||||
if (ui_node.getDescriptor() == app_desc) {
|
||||
present = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!present) {
|
||||
mUiManifestNode.appendNewUiChild(app_desc);
|
||||
}
|
||||
|
||||
app_desc = AndroidManifestDescriptors.USES_SDK_ELEMENT;
|
||||
present = false;
|
||||
for (UiElementNode ui_node : mUiManifestNode.getUiChildren()) {
|
||||
if (ui_node.getDescriptor() == app_desc) {
|
||||
present = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!present) {
|
||||
mUiManifestNode.appendNewUiChild(app_desc);
|
||||
}
|
||||
|
||||
// Add a listener to refresh the root node if the resource framework changes
|
||||
// by forcing it to parse its own XML
|
||||
mResourceRefreshListener = new Runnable() {
|
||||
public void run() {
|
||||
commitPages(false /* onSave */);
|
||||
|
||||
mUiManifestNode.reloadFromXmlNode(mUiManifestNode.getXmlNode());
|
||||
if (mOverviewPage != null) {
|
||||
mOverviewPage.refreshUiApplicationNode();
|
||||
}
|
||||
if (mAppPage != null) {
|
||||
mAppPage.refreshUiApplicationNode();
|
||||
}
|
||||
}
|
||||
};
|
||||
EditorsPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener);
|
||||
mResourceRefreshListener.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link IFile} being edited, or <code>null</code> if it couldn't be computed.
|
||||
*/
|
||||
private IFile getInputFile() {
|
||||
IEditorInput input = getEditorInput();
|
||||
if (input instanceof FileEditorInput) {
|
||||
FileEditorInput fileInput = (FileEditorInput) input;
|
||||
return fileInput.getFile();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest;
|
||||
|
||||
import org.eclipse.jface.action.IAction;
|
||||
import org.eclipse.ui.IActionBars;
|
||||
import org.eclipse.ui.IEditorPart;
|
||||
import org.eclipse.ui.actions.ActionFactory;
|
||||
import org.eclipse.ui.ide.IDEActionFactory;
|
||||
import org.eclipse.ui.part.MultiPageEditorActionBarContributor;
|
||||
import org.eclipse.ui.texteditor.ITextEditor;
|
||||
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
|
||||
|
||||
/**
|
||||
* Manages the installation/deinstallation of global actions for multi-page
|
||||
* editors. Responsible for the redirection of global actions to the active
|
||||
* editor. Multi-page contributor replaces the contributors for the individual
|
||||
* editors in the multi-page editor.
|
||||
*
|
||||
* TODO: Doesn't look like we need this. Remove it if not needed.
|
||||
* @deprecated
|
||||
*/
|
||||
final class ManifestEditorContributor extends MultiPageEditorActionBarContributor {
|
||||
private IEditorPart mActiveEditorPart;
|
||||
|
||||
/**
|
||||
* Creates a multi-page contributor.
|
||||
*
|
||||
* Marked as Private so it can't be instanciated. This is a cheap way to make sure
|
||||
* it's not being used. As noted in constructor, should be removed if not used.
|
||||
* @deprecated
|
||||
*/
|
||||
private ManifestEditorContributor() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action registed with the given text editor.
|
||||
*
|
||||
* @return IAction or null if editor is null.
|
||||
*/
|
||||
protected IAction getAction(ITextEditor editor, String actionID) {
|
||||
return (editor == null ? null : editor.getAction(actionID));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-JavaDoc) Method declared in
|
||||
* AbstractMultiPageEditorActionBarContributor.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void setActivePage(IEditorPart part) {
|
||||
if (mActiveEditorPart == part)
|
||||
return;
|
||||
|
||||
mActiveEditorPart = part;
|
||||
|
||||
IActionBars actionBars = getActionBars();
|
||||
if (actionBars != null) {
|
||||
|
||||
ITextEditor editor =
|
||||
(part instanceof ITextEditor) ? (ITextEditor)part : null;
|
||||
|
||||
actionBars.setGlobalActionHandler(ActionFactory.DELETE.getId(),
|
||||
getAction(editor, ITextEditorActionConstants.DELETE));
|
||||
actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(),
|
||||
getAction(editor, ITextEditorActionConstants.UNDO));
|
||||
actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(),
|
||||
getAction(editor, ITextEditorActionConstants.REDO));
|
||||
actionBars.setGlobalActionHandler(ActionFactory.CUT.getId(),
|
||||
getAction(editor, ITextEditorActionConstants.CUT));
|
||||
actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(),
|
||||
getAction(editor, ITextEditorActionConstants.COPY));
|
||||
actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(),
|
||||
getAction(editor, ITextEditorActionConstants.PASTE));
|
||||
actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
|
||||
getAction(editor, ITextEditorActionConstants.SELECT_ALL));
|
||||
actionBars.setGlobalActionHandler(ActionFactory.FIND.getId(),
|
||||
getAction(editor, ITextEditorActionConstants.FIND));
|
||||
actionBars.setGlobalActionHandler(
|
||||
IDEActionFactory.BOOKMARK.getId(), getAction(editor,
|
||||
IDEActionFactory.BOOKMARK.getId()));
|
||||
actionBars.updateActionBars();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest;
|
||||
|
||||
|
||||
import com.android.ide.eclipse.editors.AndroidSourceViewerConfig;
|
||||
|
||||
/**
|
||||
* Source Viewer Configuration that calls in ManifestContentAssist.
|
||||
*/
|
||||
public final class ManifestSourceViewerConfig extends AndroidSourceViewerConfig {
|
||||
|
||||
public ManifestSourceViewerConfig() {
|
||||
super(new ManifestContentAssist());
|
||||
}
|
||||
}
|
||||
@@ -1,497 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.resources.DeclareStyleableInfo;
|
||||
import com.android.ide.eclipse.common.resources.ResourceType;
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.ListAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.ReferenceAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
|
||||
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
/**
|
||||
* Complete description of the AndroidManifest.xml structure.
|
||||
* <p/>
|
||||
* The root element are static instances which always exists.
|
||||
* However their sub-elements and attributes are created only when the SDK changes or is
|
||||
* loaded the first time.
|
||||
*/
|
||||
public class AndroidManifestDescriptors {
|
||||
|
||||
private static final String MANIFEST_NODE_NAME = "manifest"; //$NON-NLS-1$
|
||||
private static final String ANDROID_MANIFEST_STYLEABLE = "AndroidManifest"; //$NON-NLS-1$
|
||||
|
||||
// Public attributes names, attributes descriptors and elements descriptors
|
||||
|
||||
public static final String ANDROID_LABEL_ATTR = "label"; //$NON-NLS-1$
|
||||
public static final String ANDROID_NAME_ATTR = "name"; //$NON-NLS-1$
|
||||
public static final String PACKAGE_ATTR = "package"; //$NON-NLS-1$
|
||||
|
||||
/** The {@link ElementDescriptor} for the root Manifest element. */
|
||||
public static final ElementDescriptor MANIFEST_ELEMENT;
|
||||
/** The {@link ElementDescriptor} for the root Application element. */
|
||||
public static final ElementDescriptor APPLICATION_ELEMENT;
|
||||
|
||||
/** The {@link ElementDescriptor} for the root Instrumentation element. */
|
||||
public static final ElementDescriptor INTRUMENTATION_ELEMENT;
|
||||
/** The {@link ElementDescriptor} for the root Permission element. */
|
||||
public static final ElementDescriptor PERMISSION_ELEMENT;
|
||||
/** The {@link ElementDescriptor} for the root UsesPermission element. */
|
||||
public static final ElementDescriptor USES_PERMISSION_ELEMENT;
|
||||
/** The {@link ElementDescriptor} for the root UsesSdk element. */
|
||||
public static final ElementDescriptor USES_SDK_ELEMENT;
|
||||
|
||||
/** The {@link ElementDescriptor} for the root PermissionGroup element. */
|
||||
public static final ElementDescriptor PERMISSION_GROUP_ELEMENT;
|
||||
/** The {@link ElementDescriptor} for the root PermissionTree element. */
|
||||
public static final ElementDescriptor PERMISSION_TREE_ELEMENT;
|
||||
|
||||
/** Private package attribute for the manifest element. Needs to be handled manually. */
|
||||
private static final TextAttributeDescriptor PACKAGE_ATTR_DESC;
|
||||
|
||||
static {
|
||||
APPLICATION_ELEMENT = createElement("application", null, true); //$NON-NLS-1$ + no child & mandatory
|
||||
INTRUMENTATION_ELEMENT = createElement("instrumentation"); //$NON-NLS-1$
|
||||
|
||||
PERMISSION_ELEMENT = createElement("permission"); //$NON-NLS-1$
|
||||
USES_PERMISSION_ELEMENT = createElement("uses-permission"); //$NON-NLS-1$
|
||||
USES_SDK_ELEMENT = createElement("uses-sdk", null, true); //$NON-NLS-1$ + no child & mandatory
|
||||
|
||||
PERMISSION_GROUP_ELEMENT = createElement("permission-group"); //$NON-NLS-1$
|
||||
PERMISSION_TREE_ELEMENT = createElement("permission-tree"); //$NON-NLS-1$
|
||||
|
||||
MANIFEST_ELEMENT = createElement(
|
||||
MANIFEST_NODE_NAME, // xml name
|
||||
new ElementDescriptor[] {
|
||||
APPLICATION_ELEMENT,
|
||||
INTRUMENTATION_ELEMENT,
|
||||
PERMISSION_ELEMENT,
|
||||
USES_PERMISSION_ELEMENT,
|
||||
PERMISSION_GROUP_ELEMENT,
|
||||
PERMISSION_TREE_ELEMENT,
|
||||
USES_SDK_ELEMENT,
|
||||
},
|
||||
true /* mandatory */);
|
||||
|
||||
// The "package" attribute is treated differently as it doesn't have the standard
|
||||
// Android XML namespace.
|
||||
PACKAGE_ATTR_DESC = new PackageAttributeDescriptor(PACKAGE_ATTR,
|
||||
"Package",
|
||||
null /* nsUri */,
|
||||
"This attribute gives a unique name for the package, using a Java-style naming convention to avoid name collisions.\nFor example, applications published by Google could have names of the form com.google.app.appname");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the document descriptor.
|
||||
* <p/>
|
||||
* It first computes the new children of the descriptor and then updates them
|
||||
* all at once.
|
||||
*
|
||||
* @param manifestMap The map style => attributes from the attrs_manifest.xml file
|
||||
*/
|
||||
public static synchronized void updateDescriptors(
|
||||
Map<String, DeclareStyleableInfo> manifestMap) {
|
||||
|
||||
XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(
|
||||
"android", //$NON-NLS-1$
|
||||
AndroidConstants.NS_RESOURCES);
|
||||
|
||||
// -- setup the various attribute format overrides --
|
||||
// The key for each override is "element1,element2,.../attr-xml-local-name" or
|
||||
// "*/attr-xml-local-name" to match the attribute in any element.
|
||||
|
||||
Map<String, Object> overrides = new HashMap<String, Object>();
|
||||
|
||||
overrides.put("*/icon", new DescriptorsUtils.ITextAttributeCreator() { //$NON-NLS-1$
|
||||
public TextAttributeDescriptor create(String xmlName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
return new ReferenceAttributeDescriptor(
|
||||
ResourceType.DRAWABLE,
|
||||
xmlName, uiName, nsUri,
|
||||
tooltip);
|
||||
}
|
||||
});
|
||||
|
||||
overrides.put("*/theme", ThemeAttributeDescriptor.class); //$NON-NLS-1$
|
||||
overrides.put("*/permission", ListAttributeDescriptor.class); //$NON-NLS-1$
|
||||
overrides.put("*/targetPackage", PackageAttributeDescriptor.class); //$NON-NLS-1$
|
||||
|
||||
overrides.put("action,category,uses-permission/" + ANDROID_NAME_ATTR, //$NON-NLS-1$
|
||||
ListAttributeDescriptor.class);
|
||||
overrides.put("application/" + ANDROID_NAME_ATTR, ApplicationAttributeDescriptor.class); //$NON-NLS-1$
|
||||
|
||||
overrideClassName(overrides, "activity", AndroidConstants.CLASS_ACTIVITY); //$NON-NLS-1$
|
||||
overrideClassName(overrides, "receiver", AndroidConstants.CLASS_BROADCASTRECEIVER); //$NON-NLS-1$
|
||||
overrideClassName(overrides, "service", AndroidConstants.CLASS_SERVICE); //$NON-NLS-1$
|
||||
overrideClassName(overrides, "provider", AndroidConstants.CLASS_CONTENTPROVIDER); //$NON-NLS-1$
|
||||
|
||||
// -- list element nodes already created --
|
||||
// These elements are referenced by already opened editors, so we want to update them
|
||||
// but not re-create them when reloading an SDK on the fly.
|
||||
|
||||
HashMap<String, ElementDescriptor> elementDescs =
|
||||
new HashMap<String, ElementDescriptor>();
|
||||
elementDescs.put(MANIFEST_ELEMENT.getXmlLocalName(), MANIFEST_ELEMENT);
|
||||
elementDescs.put(APPLICATION_ELEMENT.getXmlLocalName(), APPLICATION_ELEMENT);
|
||||
elementDescs.put(INTRUMENTATION_ELEMENT.getXmlLocalName(), INTRUMENTATION_ELEMENT);
|
||||
elementDescs.put(PERMISSION_ELEMENT.getXmlLocalName(), PERMISSION_ELEMENT);
|
||||
elementDescs.put(USES_PERMISSION_ELEMENT.getXmlLocalName(), USES_PERMISSION_ELEMENT);
|
||||
elementDescs.put(USES_SDK_ELEMENT.getXmlLocalName(), USES_SDK_ELEMENT);
|
||||
elementDescs.put(PERMISSION_GROUP_ELEMENT.getXmlLocalName(), PERMISSION_GROUP_ELEMENT);
|
||||
elementDescs.put(PERMISSION_TREE_ELEMENT.getXmlLocalName(), PERMISSION_TREE_ELEMENT);
|
||||
|
||||
// --
|
||||
|
||||
inflateElement(manifestMap, overrides, elementDescs,
|
||||
MANIFEST_ELEMENT, "AndroidManifest"); //$NON-NLS-1$
|
||||
insertAttribute(MANIFEST_ELEMENT, PACKAGE_ATTR_DESC);
|
||||
|
||||
sanityCheck(manifestMap, MANIFEST_ELEMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an attribute override for ANDROID_NAME_ATTR using a ClassAttributeDescriptor
|
||||
* with the specified class name.
|
||||
*/
|
||||
private static void overrideClassName(Map<String, Object> overrides,
|
||||
String elementName, final String className) {
|
||||
overrides.put(elementName + "/" + ANDROID_NAME_ATTR,
|
||||
new DescriptorsUtils.ITextAttributeCreator() {
|
||||
public TextAttributeDescriptor create(String xmlName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
if (AndroidConstants.CLASS_ACTIVITY.equals(className)) {
|
||||
return new ClassAttributeDescriptor(
|
||||
className,
|
||||
PostActivityCreationAction.getAction(),
|
||||
xmlName, uiName + "*", //$NON-NLS-1$
|
||||
nsUri,
|
||||
tooltip,
|
||||
true /*mandatory */);
|
||||
} else if (AndroidConstants.CLASS_BROADCASTRECEIVER.equals(className)) {
|
||||
return new ClassAttributeDescriptor(
|
||||
className,
|
||||
PostReceiverCreationAction.getAction(),
|
||||
xmlName, uiName + "*", //$NON-NLS-1$
|
||||
nsUri,
|
||||
tooltip,
|
||||
true /*mandatory */);
|
||||
|
||||
} else {
|
||||
return new ClassAttributeDescriptor(
|
||||
className,
|
||||
xmlName, uiName + "*", //$NON-NLS-1$
|
||||
nsUri,
|
||||
tooltip,
|
||||
true /*mandatory */);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new ElementDescriptor constructed from the information given here
|
||||
* and the javadoc & attributes extracted from the style map if any.
|
||||
* <p/>
|
||||
* Creates an element with no attribute overrides.
|
||||
*/
|
||||
private static ElementDescriptor createElement(
|
||||
String xmlName,
|
||||
ElementDescriptor[] childrenElements,
|
||||
boolean mandatory) {
|
||||
// Creates an element with no attribute overrides.
|
||||
String styleName = guessStyleName(xmlName);
|
||||
String sdkUrl = DescriptorsUtils.MANIFEST_SDK_URL + styleName;
|
||||
String uiName = getUiName(xmlName);
|
||||
|
||||
ElementDescriptor element = new ManifestElementDescriptor(xmlName, uiName, null, sdkUrl,
|
||||
null, childrenElements, mandatory);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new ElementDescriptor constructed from its XML local name.
|
||||
* <p/>
|
||||
* This version creates an element not mandatory.
|
||||
*/
|
||||
private static ElementDescriptor createElement(String xmlName) {
|
||||
// Creates an element with no child and not mandatory
|
||||
return createElement(xmlName, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an attribute in this element attribute list if it is not present there yet
|
||||
* (based on the attribute XML name.)
|
||||
* The attribute is inserted at the beginning of the attribute list.
|
||||
*/
|
||||
private static void insertAttribute(ElementDescriptor element, AttributeDescriptor newAttr) {
|
||||
AttributeDescriptor[] attributes = element.getAttributes();
|
||||
for (AttributeDescriptor attr : attributes) {
|
||||
if (attr.getXmlLocalName().equals(newAttr.getXmlLocalName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AttributeDescriptor[] newArray = new AttributeDescriptor[attributes.length + 1];
|
||||
newArray[0] = newAttr;
|
||||
System.arraycopy(attributes, 0, newArray, 1, attributes.length);
|
||||
element.setAttributes(newArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* "Inflates" the properties of an {@link ElementDescriptor} from the styleable declaration.
|
||||
* <p/>
|
||||
* This first creates all the attributes for the given ElementDescriptor.
|
||||
* It then find all children of the descriptor, inflate them recursively and set them
|
||||
* as child to this ElementDescriptor.
|
||||
*
|
||||
* @param styleMap The input styleable map for manifest elements & attributes
|
||||
* @param overrides A list of attribute overrides (to customize the type of the attribute
|
||||
* descriptors)
|
||||
* @param existingElementDescs A map of already created element descriptors, keyed by
|
||||
* XML local name. This is used to use the static elements created initially by this
|
||||
* class, which are referenced directly by editors (so that reloading an SDK won't
|
||||
* break these references)
|
||||
* @param elemDesc The current {@link ElementDescriptor} to inflate.
|
||||
* @param styleName The name of the {@link ElementDescriptor} to inflate. Its XML local name
|
||||
* will be guessed automatically from the style name.
|
||||
*/
|
||||
private static void inflateElement(
|
||||
Map<String, DeclareStyleableInfo> styleMap,
|
||||
Map<String, Object> overrides,
|
||||
HashMap<String, ElementDescriptor> existingElementDescs,
|
||||
ElementDescriptor elemDesc,
|
||||
String styleName) {
|
||||
assert elemDesc != null;
|
||||
assert styleName != null;
|
||||
|
||||
// define attributes
|
||||
DeclareStyleableInfo style = styleMap != null ? styleMap.get(styleName) : null;
|
||||
if (style != null) {
|
||||
ArrayList<AttributeDescriptor> attrDescs = new ArrayList<AttributeDescriptor>();
|
||||
DescriptorsUtils.appendAttributes(attrDescs,
|
||||
elemDesc.getXmlLocalName(),
|
||||
AndroidConstants.NS_RESOURCES,
|
||||
style.getAttributes(), null, overrides);
|
||||
elemDesc.setTooltip(style.getJavaDoc());
|
||||
elemDesc.setAttributes(attrDescs.toArray(new AttributeDescriptor[attrDescs.size()]));
|
||||
}
|
||||
|
||||
// find all elements that have this one as parent
|
||||
ArrayList<ElementDescriptor> children = new ArrayList<ElementDescriptor>();
|
||||
for (Entry<String, DeclareStyleableInfo> entry : styleMap.entrySet()) {
|
||||
DeclareStyleableInfo childStyle = entry.getValue();
|
||||
boolean isParent = false;
|
||||
String[] parents = childStyle.getParents();
|
||||
if (parents != null) {
|
||||
for (String parent: parents) {
|
||||
if (styleName.equals(parent)) {
|
||||
isParent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isParent) {
|
||||
String childStyleName = entry.getKey();
|
||||
String childXmlName = guessXmlName(childStyleName);
|
||||
|
||||
// create or re-use element
|
||||
ElementDescriptor child = existingElementDescs.get(childXmlName);
|
||||
if (child == null) {
|
||||
child = createElement(childXmlName);
|
||||
existingElementDescs.put(childXmlName, child);
|
||||
}
|
||||
children.add(child);
|
||||
|
||||
inflateElement(styleMap, overrides, existingElementDescs, child, childStyleName);
|
||||
}
|
||||
}
|
||||
elemDesc.setChildren(children.toArray(new ElementDescriptor[children.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an UI name from the element XML name.
|
||||
* <p/>
|
||||
* Capitalizes the first letter and replace non-alphabet by a space followed by a capital.
|
||||
*/
|
||||
private static String getUiName(String xmlName) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
boolean capitalize = true;
|
||||
for (char c : xmlName.toCharArray()) {
|
||||
if (capitalize && c >= 'a' && c <= 'z') {
|
||||
sb.append((char)(c + 'A' - 'a'));
|
||||
capitalize = false;
|
||||
} else if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
|
||||
sb.append(' ');
|
||||
capitalize = true;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Guesses the style name for a given XML element name.
|
||||
* <p/>
|
||||
* The rules are:
|
||||
* - capitalize the first letter:
|
||||
* - if there's a dash, skip it and capitalize the next one
|
||||
* - prefix AndroidManifest
|
||||
* The exception is "manifest" which just becomes AndroidManifest.
|
||||
* <p/>
|
||||
* Examples:
|
||||
* - manifest => AndroidManifest
|
||||
* - application => AndroidManifestApplication
|
||||
* - uses-permission => AndroidManifestUsesPermission
|
||||
*/
|
||||
private static String guessStyleName(String xmlName) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (!xmlName.equals(MANIFEST_NODE_NAME)) {
|
||||
boolean capitalize = true;
|
||||
for (char c : xmlName.toCharArray()) {
|
||||
if (capitalize && c >= 'a' && c <= 'z') {
|
||||
sb.append((char)(c + 'A' - 'a'));
|
||||
capitalize = false;
|
||||
} else if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
|
||||
// not a letter -- skip the character and capitalize the next one
|
||||
capitalize = true;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sb.insert(0, ANDROID_MANIFEST_STYLEABLE);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method performs a sanity check to make sure all the styles declared in the
|
||||
* manifestMap are actually defined in the actual element descriptors and reachable from
|
||||
* the manifestElement root node.
|
||||
*/
|
||||
private static void sanityCheck(Map<String, DeclareStyleableInfo> manifestMap,
|
||||
ElementDescriptor manifestElement) {
|
||||
TreeSet<String> elementsDeclared = new TreeSet<String>();
|
||||
findAllElementNames(manifestElement, elementsDeclared);
|
||||
|
||||
TreeSet<String> stylesDeclared = new TreeSet<String>();
|
||||
for (String styleName : manifestMap.keySet()) {
|
||||
if (styleName.startsWith(ANDROID_MANIFEST_STYLEABLE)) {
|
||||
stylesDeclared.add(styleName);
|
||||
}
|
||||
}
|
||||
|
||||
for (Iterator<String> it = elementsDeclared.iterator(); it.hasNext();) {
|
||||
String xmlName = it.next();
|
||||
String styleName = guessStyleName(xmlName);
|
||||
if (stylesDeclared.remove(styleName)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!stylesDeclared.isEmpty()) {
|
||||
sb.append("Warning, ADT/SDK Mismatch! The following elements are declared by the SDK but unknown to ADT: ");
|
||||
for (String name : stylesDeclared) {
|
||||
name = guessXmlName(name);
|
||||
|
||||
if (name != stylesDeclared.last()) {
|
||||
sb.append(", "); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
EditorsPlugin.log(IStatus.WARNING, "%s", sb.toString());
|
||||
EditorsPlugin.printToConsole(null, sb);
|
||||
sb.setLength(0);
|
||||
}
|
||||
|
||||
if (!elementsDeclared.isEmpty()) {
|
||||
sb.append("Warning, ADT/SDK Mismatch! The following elements are declared by ADT but not by the SDK: ");
|
||||
for (String name : elementsDeclared) {
|
||||
sb.append(name);
|
||||
if (name != elementsDeclared.last()) {
|
||||
sb.append(", "); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
EditorsPlugin.log(IStatus.WARNING, "%s", sb.toString());
|
||||
EditorsPlugin.printToConsole(null, sb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an approximate translation of the style name into a potential
|
||||
* xml name. This is more or less the reverse from guessStyleName().
|
||||
*
|
||||
* @return The XML local name for a given style name.
|
||||
*/
|
||||
private static String guessXmlName(String name) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (ANDROID_MANIFEST_STYLEABLE.equals(name)) {
|
||||
sb.append(MANIFEST_NODE_NAME);
|
||||
} else {
|
||||
name = name.replace(ANDROID_MANIFEST_STYLEABLE, ""); //$NON-NLS-1$
|
||||
boolean first_char = true;
|
||||
for (char c : name.toCharArray()) {
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
if (!first_char) {
|
||||
sb.append('-');
|
||||
}
|
||||
c = (char) (c - 'A' + 'a');
|
||||
}
|
||||
sb.append(c);
|
||||
first_char = false;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method used by {@link #sanityCheck(Map, ElementDescriptor)} to find all the
|
||||
* {@link ElementDescriptor} names defined by the tree of descriptors.
|
||||
* <p/>
|
||||
* Note: this assumes no circular reference in the tree of {@link ElementDescriptor}s.
|
||||
*/
|
||||
private static void findAllElementNames(ElementDescriptor element, TreeSet<String> declared) {
|
||||
declared.add(element.getXmlName());
|
||||
for (ElementDescriptor desc : element.getChildren()) {
|
||||
findAllElementNames(desc, declared);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
/**
|
||||
* Describes an 'Application' class XML attribute. It is displayed by a
|
||||
* {@link UiClassAttributeNode}, that restricts creation and selection to classes
|
||||
* inheriting from android.app.Application.
|
||||
*/
|
||||
public class ApplicationAttributeDescriptor extends TextAttributeDescriptor {
|
||||
|
||||
public ApplicationAttributeDescriptor(String xmlLocalName, String uiName,
|
||||
String nsUri, String tooltip) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiClassAttributeNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiClassAttributeNode("android.app.Application", //$NON-NLS-1$
|
||||
null /* postCreationAction */, false /* mandatory */, this, uiParent);
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode;
|
||||
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode.IPostTypeCreationAction;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
/**
|
||||
* Describes an XML attribute representing a class name.
|
||||
* It is displayed by a {@link UiClassAttributeNode}.
|
||||
*/
|
||||
public class ClassAttributeDescriptor extends TextAttributeDescriptor {
|
||||
|
||||
/** Superclass of the class value. */
|
||||
private String mSuperClassName;
|
||||
|
||||
private IPostTypeCreationAction mPostCreationAction;
|
||||
|
||||
/** indicates if the class parameter is mandatory */
|
||||
boolean mMandatory;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ClassAttributeDescriptor}
|
||||
* @param superClassName the fully qualified name of the superclass of the class represented
|
||||
* by the attribute.
|
||||
* @param xmlLocalName The XML name of the attribute (case sensitive, with android: prefix).
|
||||
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
|
||||
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
|
||||
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
|
||||
* @param tooltip A non-empty tooltip string or null.
|
||||
* @param mandatory indicates if the class attribute is mandatory.
|
||||
*/
|
||||
public ClassAttributeDescriptor(String superClassName,
|
||||
String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip, boolean mandatory) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
mSuperClassName = superClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ClassAttributeDescriptor}
|
||||
* @param superClassName the fully qualified name of the superclass of the class represented
|
||||
* by the attribute.
|
||||
* @param postCreationAction the {@link IPostTypeCreationAction} to be executed on the
|
||||
* newly created class.
|
||||
* @param xmlLocalName The XML local name of the attribute (case sensitive).
|
||||
* @param uiName The UI name of the attribute. Cannot be an empty string and cannot be null.
|
||||
* @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
|
||||
* See {@link AndroidConstants#NS_RESOURCES} for a common value.
|
||||
* @param tooltip A non-empty tooltip string or null.
|
||||
* @param mandatory indicates if the class attribute is mandatory.
|
||||
*/
|
||||
public ClassAttributeDescriptor(String superClassName,
|
||||
IPostTypeCreationAction postCreationAction,
|
||||
String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip, boolean mandatory) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
mSuperClassName = superClassName;
|
||||
mPostCreationAction = postCreationAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiClassAttributeNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiClassAttributeNode(mSuperClassName, mPostCreationAction,
|
||||
mMandatory, this, uiParent);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
/**
|
||||
* Describes a 'Instrumentation' class XML attribute. It is displayed by a
|
||||
* {@link UiClassAttributeNode}, that restricts creation and selection to classes inheriting from
|
||||
* android.app.Instrumentation.
|
||||
*/
|
||||
public class InstrumentationAttributeDescriptor extends TextAttributeDescriptor {
|
||||
|
||||
public InstrumentationAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiClassAttributeNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiClassAttributeNode(AndroidConstants.CLASS_INSTRUMENTATION,
|
||||
null /* postCreationAction */, true /* mandatory */, this, uiParent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.model.UiManifestElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
/**
|
||||
* {@link ManifestElementDescriptor} describes an XML element node, with its
|
||||
* element name, its possible attributes, its possible child elements but also
|
||||
* its display name and tooltip.
|
||||
*
|
||||
* This {@link ElementDescriptor} is specialized to create {@link UiManifestElementNode} UI nodes.
|
||||
*/
|
||||
public class ManifestElementDescriptor extends ElementDescriptor {
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ManifestElementDescriptor}.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
* @param ui_name The XML element name for the user interface, typically capitalized.
|
||||
* @param tooltip An optional tooltip. Can be null or empty.
|
||||
* @param sdk_url An optional SKD URL. Can be null or empty.
|
||||
* @param attributes The list of allowed attributes. Can be null or empty.
|
||||
* @param children The list of allowed children. Can be null or empty.
|
||||
* @param mandatory Whether this node must always exist (even for empty models).
|
||||
*/
|
||||
public ManifestElementDescriptor(String xml_name, String ui_name, String tooltip, String sdk_url,
|
||||
AttributeDescriptor[] attributes,
|
||||
ElementDescriptor[] children,
|
||||
boolean mandatory) {
|
||||
super(xml_name, ui_name, tooltip, sdk_url, attributes, children, mandatory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link ManifestElementDescriptor}.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
* @param ui_name The XML element name for the user interface, typically capitalized.
|
||||
* @param tooltip An optional tooltip. Can be null or empty.
|
||||
* @param sdk_url An optional SKD URL. Can be null or empty.
|
||||
* @param attributes The list of allowed attributes. Can be null or empty.
|
||||
* @param children The list of allowed children. Can be null or empty.
|
||||
*/
|
||||
public ManifestElementDescriptor(String xml_name, String ui_name, String tooltip, String sdk_url,
|
||||
AttributeDescriptor[] attributes,
|
||||
ElementDescriptor[] children) {
|
||||
super(xml_name, ui_name, tooltip, sdk_url, attributes, children, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a shortcut for
|
||||
* ManifestElementDescriptor(xml_name, xml_name.capitalize(), null, null, null, children).
|
||||
* This constructor is mostly used for unit tests.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
*/
|
||||
public ManifestElementDescriptor(String xml_name, ElementDescriptor[] children) {
|
||||
super(xml_name, children);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a shortcut for
|
||||
* ManifestElementDescriptor(xml_name, xml_name.capitalize(), null, null, null, null).
|
||||
* This constructor is mostly used for unit tests.
|
||||
*
|
||||
* @param xml_name The XML element node name. Case sensitive.
|
||||
*/
|
||||
public ManifestElementDescriptor(String xml_name) {
|
||||
super(xml_name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiElementNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiElementNode createUiNode() {
|
||||
return new UiManifestElementNode(this);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.model.UiPackageAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
/**
|
||||
* Describes a package XML attribute. It is displayed by a {@link UiPackageAttributeNode}.
|
||||
*/
|
||||
public class PackageAttributeDescriptor extends TextAttributeDescriptor {
|
||||
|
||||
public PackageAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiPackageAttributeNode} linked to this descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiPackageAttributeNode(this, uiParent);
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode.IPostTypeCreationAction;
|
||||
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.jdt.core.ICompilationUnit;
|
||||
import org.eclipse.jdt.core.IJavaElement;
|
||||
import org.eclipse.jdt.core.IType;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
|
||||
/**
|
||||
* Action to be executed after an Activity class is created.
|
||||
*/
|
||||
class PostActivityCreationAction implements IPostTypeCreationAction {
|
||||
|
||||
private final static PostActivityCreationAction sAction = new PostActivityCreationAction();
|
||||
|
||||
private PostActivityCreationAction() {
|
||||
// private constructor to enforce singleton.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the action.
|
||||
*/
|
||||
public static IPostTypeCreationAction getAction() {
|
||||
return sAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a newly created Activity.
|
||||
*
|
||||
*/
|
||||
public void processNewType(IType newType) {
|
||||
try {
|
||||
String methodContent =
|
||||
" /** Called when the activity is first created. */\n" +
|
||||
" @Override\n" +
|
||||
" public void onCreate(Bundle savedInstanceState) {\n" +
|
||||
" super.onCreate(savedInstanceState);\n" +
|
||||
"\n" +
|
||||
" // TODO Auto-generated method stub\n" +
|
||||
" }";
|
||||
newType.createMethod(methodContent, null /* sibling*/, false /* force */,
|
||||
new NullProgressMonitor());
|
||||
|
||||
// we need to add the import for Bundle, so we need the compilation unit.
|
||||
// Since the type could be enclosed in other types, we loop till we find it.
|
||||
ICompilationUnit compilationUnit = null;
|
||||
IJavaElement element = newType;
|
||||
do {
|
||||
IJavaElement parentElement = element.getParent();
|
||||
if (parentElement != null) {
|
||||
if (parentElement.getElementType() == IJavaElement.COMPILATION_UNIT) {
|
||||
compilationUnit = (ICompilationUnit)parentElement;
|
||||
}
|
||||
|
||||
element = parentElement;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (compilationUnit == null);
|
||||
|
||||
if (compilationUnit != null) {
|
||||
compilationUnit.createImport(AndroidConstants.CLASS_BUNDLE,
|
||||
null /* sibling */, new NullProgressMonitor());
|
||||
}
|
||||
} catch (JavaModelException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode.IPostTypeCreationAction;
|
||||
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.jdt.core.ICompilationUnit;
|
||||
import org.eclipse.jdt.core.IJavaElement;
|
||||
import org.eclipse.jdt.core.IType;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
|
||||
/**
|
||||
* Action to be executed after an BroadcastReceiver class is created.
|
||||
*/
|
||||
class PostReceiverCreationAction implements IPostTypeCreationAction {
|
||||
|
||||
private final static PostReceiverCreationAction sAction = new PostReceiverCreationAction();
|
||||
|
||||
private PostReceiverCreationAction() {
|
||||
// private constructor to enforce singleton.
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the action.
|
||||
*/
|
||||
public static IPostTypeCreationAction getAction() {
|
||||
return sAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a newly created Activity.
|
||||
*
|
||||
*/
|
||||
public void processNewType(IType newType) {
|
||||
try {
|
||||
String methodContent =
|
||||
" @Override\n" +
|
||||
" public void onReceive(Context context, Intent intent) {\n" +
|
||||
" // TODO Auto-generated method stub\n" +
|
||||
" }";
|
||||
newType.createMethod(methodContent, null /* sibling*/, false /* force */,
|
||||
new NullProgressMonitor());
|
||||
|
||||
// we need to add the import for Bundle, so we need the compilation unit.
|
||||
// Since the type could be enclosed in other types, we loop till we find it.
|
||||
ICompilationUnit compilationUnit = null;
|
||||
IJavaElement element = newType;
|
||||
do {
|
||||
IJavaElement parentElement = element.getParent();
|
||||
if (parentElement != null) {
|
||||
if (parentElement.getElementType() == IJavaElement.COMPILATION_UNIT) {
|
||||
compilationUnit = (ICompilationUnit)parentElement;
|
||||
}
|
||||
|
||||
element = parentElement;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (compilationUnit == null);
|
||||
|
||||
if (compilationUnit != null) {
|
||||
compilationUnit.createImport(AndroidConstants.CLASS_CONTEXT,
|
||||
null /* sibling */, new NullProgressMonitor());
|
||||
compilationUnit.createImport(AndroidConstants.CLASS_INTENT,
|
||||
null /* sibling */, new NullProgressMonitor());
|
||||
}
|
||||
} catch (JavaModelException e) {
|
||||
// looks like the class already existed (this happens when the user check to create
|
||||
// inherited abstract methods).
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.descriptors;
|
||||
|
||||
import com.android.ide.eclipse.common.resources.ResourceType;
|
||||
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiResourceAttributeNode;
|
||||
|
||||
/**
|
||||
* Describes a Theme/Style XML attribute displayed by a {@link UiResourceAttributeNode}
|
||||
*/
|
||||
public final class ThemeAttributeDescriptor extends TextAttributeDescriptor {
|
||||
|
||||
public ThemeAttributeDescriptor(String xmlLocalName, String uiName, String nsUri,
|
||||
String tooltip) {
|
||||
super(xmlLocalName, uiName, nsUri, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A new {@link UiResourceAttributeNode} linked to this theme descriptor.
|
||||
*/
|
||||
@Override
|
||||
public UiAttributeNode createUiNode(UiElementNode uiParent) {
|
||||
return new UiResourceAttributeNode(ResourceType.STYLE, this, uiParent);
|
||||
}
|
||||
}
|
||||
@@ -1,624 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.model;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
import com.android.ide.eclipse.editors.AndroidEditor;
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.ui.SectionHelper;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiTextAttributeNode;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||
import org.eclipse.jdt.core.IClasspathEntry;
|
||||
import org.eclipse.jdt.core.IJavaElement;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.IPackageFragment;
|
||||
import org.eclipse.jdt.core.IPackageFragmentRoot;
|
||||
import org.eclipse.jdt.core.IType;
|
||||
import org.eclipse.jdt.core.ITypeHierarchy;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
import org.eclipse.jdt.core.search.IJavaSearchScope;
|
||||
import org.eclipse.jdt.core.search.SearchEngine;
|
||||
import org.eclipse.jdt.ui.IJavaElementSearchConstants;
|
||||
import org.eclipse.jdt.ui.JavaUI;
|
||||
import org.eclipse.jdt.ui.actions.OpenNewClassWizardAction;
|
||||
import org.eclipse.jdt.ui.dialogs.ITypeInfoFilterExtension;
|
||||
import org.eclipse.jdt.ui.dialogs.ITypeInfoRequestor;
|
||||
import org.eclipse.jdt.ui.dialogs.ITypeSelectionComponent;
|
||||
import org.eclipse.jdt.ui.dialogs.TypeSelectionExtension;
|
||||
import org.eclipse.jdt.ui.wizards.NewClassWizardPage;
|
||||
import org.eclipse.jface.dialogs.IMessageProvider;
|
||||
import org.eclipse.jface.window.Window;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.DisposeEvent;
|
||||
import org.eclipse.swt.events.DisposeListener;
|
||||
import org.eclipse.swt.events.ModifyEvent;
|
||||
import org.eclipse.swt.events.ModifyListener;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
import org.eclipse.ui.IEditorInput;
|
||||
import org.eclipse.ui.IFileEditorInput;
|
||||
import org.eclipse.ui.PartInitException;
|
||||
import org.eclipse.ui.PlatformUI;
|
||||
import org.eclipse.ui.dialogs.SelectionDialog;
|
||||
import org.eclipse.ui.forms.IManagedForm;
|
||||
import org.eclipse.ui.forms.events.HyperlinkAdapter;
|
||||
import org.eclipse.ui.forms.events.HyperlinkEvent;
|
||||
import org.eclipse.ui.forms.widgets.FormText;
|
||||
import org.eclipse.ui.forms.widgets.FormToolkit;
|
||||
import org.eclipse.ui.forms.widgets.TableWrapData;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Represents an XML attribute for a class, that can be modified using a simple text field or
|
||||
* a dialog to choose an existing class. Also, there's a link to create a new class.
|
||||
* <p/>
|
||||
* See {@link UiTextAttributeNode} for more information.
|
||||
*/
|
||||
public class UiClassAttributeNode extends UiTextAttributeNode {
|
||||
|
||||
private String mReferenceClass;
|
||||
private IPostTypeCreationAction mPostCreationAction;
|
||||
private boolean mMandatory;
|
||||
|
||||
private class HierarchyTypeSelection extends TypeSelectionExtension {
|
||||
|
||||
private IJavaProject mJavaProject;
|
||||
|
||||
public HierarchyTypeSelection(IProject project, String referenceClass) {
|
||||
mJavaProject = JavaCore.create(project);
|
||||
mReferenceClass = referenceClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITypeInfoFilterExtension getFilterExtension() {
|
||||
return new ITypeInfoFilterExtension() {
|
||||
public boolean select(ITypeInfoRequestor typeInfoRequestor) {
|
||||
String packageName = typeInfoRequestor.getPackageName();
|
||||
String typeName = typeInfoRequestor.getTypeName();
|
||||
String enclosingType = typeInfoRequestor.getEnclosingName();
|
||||
|
||||
// build the full class name.
|
||||
StringBuilder sb = new StringBuilder(packageName);
|
||||
sb.append('.');
|
||||
if (enclosingType.length() > 0) {
|
||||
sb.append(enclosingType);
|
||||
sb.append('.');
|
||||
}
|
||||
sb.append(typeName);
|
||||
|
||||
String className = sb.toString();
|
||||
|
||||
try {
|
||||
IType type = mJavaProject.findType(className);
|
||||
if (type != null) {
|
||||
// get the type hierarchy
|
||||
ITypeHierarchy hierarchy = type.newSupertypeHierarchy(
|
||||
new NullProgressMonitor());
|
||||
|
||||
// if the super class is not the reference class, it may inherit from
|
||||
// it so we get its supertype. At some point it will be null and we
|
||||
// will return false;
|
||||
IType superType = type;
|
||||
while ((superType = hierarchy.getSuperclass(superType)) != null) {
|
||||
if (mReferenceClass.equals(superType.getFullyQualifiedName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JavaModelException e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes which implement this interface provide a method processing newly created classes.
|
||||
*/
|
||||
public static interface IPostTypeCreationAction {
|
||||
/**
|
||||
* Sent to process a newly created class.
|
||||
* @param newType the IType representing the newly created class.
|
||||
*/
|
||||
public void processNewType(IType newType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link UiClassAttributeNode} object that will display ui to select or create
|
||||
* classes.
|
||||
* @param referenceClass The allowed supertype of the classes that are to be selected
|
||||
* or created. Can be null.
|
||||
* @param postCreationAction a {@link IPostTypeCreationAction} object handling post creation
|
||||
* modification of the class.
|
||||
* @param mandatory indicates if the class value is mandatory
|
||||
* @param attributeDescriptor the {@link AttributeDescriptor} object linked to the Ui Node.
|
||||
*/
|
||||
public UiClassAttributeNode(String referenceClass, IPostTypeCreationAction postCreationAction,
|
||||
boolean mandatory, AttributeDescriptor attributeDescriptor, UiElementNode uiParent) {
|
||||
super(attributeDescriptor, uiParent);
|
||||
|
||||
mReferenceClass = referenceClass;
|
||||
mPostCreationAction = postCreationAction;
|
||||
mMandatory = mandatory;
|
||||
}
|
||||
|
||||
/* (non-java doc)
|
||||
* Creates a label widget and an associated text field.
|
||||
* <p/>
|
||||
* As most other parts of the android manifest editor, this assumes the
|
||||
* parent uses a table layout with 2 columns.
|
||||
*/
|
||||
@Override
|
||||
public void createUiControl(final Composite parent, IManagedForm managedForm) {
|
||||
setManagedForm(managedForm);
|
||||
FormToolkit toolkit = managedForm.getToolkit();
|
||||
TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor();
|
||||
|
||||
StringBuilder label = new StringBuilder();
|
||||
label.append("<form><p><a href='unused'>");
|
||||
label.append(desc.getUiName());
|
||||
label.append("</a></p></form>");
|
||||
FormText formText = SectionHelper.createFormText(parent, toolkit, true /* isHtml */,
|
||||
label.toString(), true /* setupLayoutData */);
|
||||
formText.addHyperlinkListener(new HyperlinkAdapter() {
|
||||
@Override
|
||||
public void linkActivated(HyperlinkEvent e) {
|
||||
super.linkActivated(e);
|
||||
handleLabelClick();
|
||||
}
|
||||
});
|
||||
formText.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE));
|
||||
SectionHelper.addControlTooltip(formText, desc.getTooltip());
|
||||
|
||||
Composite composite = toolkit.createComposite(parent);
|
||||
composite.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE));
|
||||
GridLayout gl = new GridLayout(2, false);
|
||||
gl.marginHeight = gl.marginWidth = 0;
|
||||
composite.setLayout(gl);
|
||||
// Fixes missing text borders under GTK... also requires adding a 1-pixel margin
|
||||
// for the text field below
|
||||
toolkit.paintBordersFor(composite);
|
||||
|
||||
final Text text = toolkit.createText(composite, getCurrentValue());
|
||||
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
gd.horizontalIndent = 1; // Needed by the fixed composite borders under GTK
|
||||
text.setLayoutData(gd);
|
||||
Button browseButton = toolkit.createButton(composite, "Browse...", SWT.PUSH);
|
||||
|
||||
setTextWidget(text);
|
||||
|
||||
browseButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
super.widgetSelected(e);
|
||||
handleBrowseClick();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* (non-java doc)
|
||||
*
|
||||
* Add a modify listener that will check the validity of the class
|
||||
*/
|
||||
@Override
|
||||
protected void onAddValidators(final Text text) {
|
||||
ModifyListener listener = new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
try {
|
||||
String textValue = text.getText().trim();
|
||||
if (textValue.length() == 0) {
|
||||
if (mMandatory) {
|
||||
setErrorMessage("Value is mandatory", text);
|
||||
} else {
|
||||
setErrorMessage(null, text);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// first we need the current java package.
|
||||
String javaPackage = getManifestPackage();
|
||||
|
||||
// build the fully qualified name of the class
|
||||
String className = AndroidManifestHelper.combinePackageAndClassName(javaPackage,
|
||||
textValue);
|
||||
|
||||
// only test the vilibility for activities.
|
||||
boolean testVisibility = AndroidConstants.CLASS_ACTIVITY.equals(
|
||||
mReferenceClass);
|
||||
|
||||
// test the class
|
||||
setErrorMessage(BaseProjectHelper.testClassForManifest(
|
||||
BaseProjectHelper.getJavaProject(getProject()), className,
|
||||
mReferenceClass, testVisibility), text);
|
||||
} catch (CoreException ce) {
|
||||
setErrorMessage(ce.getMessage(), text);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
text.addModifyListener(listener);
|
||||
|
||||
// Make sure the validator removes its message(s) when the widget is disposed
|
||||
text.addDisposeListener(new DisposeListener() {
|
||||
public void widgetDisposed(DisposeEvent e) {
|
||||
// we don't want to use setErrorMessage, because we don't want to reset
|
||||
// the error flag in the UiAttributeNode
|
||||
getManagedForm().getMessageManager().removeMessage(text, text);
|
||||
}
|
||||
});
|
||||
|
||||
// Finally call the validator once to make sure the initial value is processed
|
||||
listener.modifyText(null);
|
||||
}
|
||||
|
||||
private void handleBrowseClick() {
|
||||
Text text = getTextWidget();
|
||||
|
||||
// we need to get the project of the manifest.
|
||||
IProject project = getProject();
|
||||
if (project != null) {
|
||||
|
||||
// Create a search scope including only the source folder of the current
|
||||
// project.
|
||||
IJavaSearchScope scope = SearchEngine.createJavaSearchScope(
|
||||
getPackageFragmentRoots(project), false);
|
||||
|
||||
try {
|
||||
SelectionDialog dlg = JavaUI.createTypeDialog(text.getShell(),
|
||||
PlatformUI.getWorkbench().getProgressService(),
|
||||
scope,
|
||||
IJavaElementSearchConstants.CONSIDER_CLASSES, // style
|
||||
false, // no multiple selection
|
||||
"**", //$NON-NLS-1$ //filter
|
||||
new HierarchyTypeSelection(project, mReferenceClass));
|
||||
dlg.setMessage(String.format("Select class name for element %1$s:",
|
||||
getUiParent().getBreadcrumbTrailDescription(false /* include_root */)));
|
||||
if (dlg instanceof ITypeSelectionComponent) {
|
||||
((ITypeSelectionComponent)dlg).triggerSearch();
|
||||
}
|
||||
|
||||
if (dlg.open() == Window.OK) {
|
||||
Object[] results = dlg.getResult();
|
||||
if (results.length == 1) {
|
||||
handleNewType((IType)results[0]);
|
||||
}
|
||||
}
|
||||
} catch (JavaModelException e1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLabelClick() {
|
||||
// get the current value
|
||||
String className = getTextWidget().getText().trim();
|
||||
|
||||
// get the package name from the manifest.
|
||||
String packageName = getManifestPackage();
|
||||
|
||||
if (className.length() == 0) {
|
||||
createNewClass(packageName, null /* className */);
|
||||
} else {
|
||||
// build back the fully qualified class name.
|
||||
String fullClassName = className;
|
||||
if (className.startsWith(".")) { //$NON-NLS-1$
|
||||
fullClassName = packageName + className;
|
||||
} else {
|
||||
String[] segments = className.split(AndroidConstants.RE_DOT);
|
||||
if (segments.length == 1) {
|
||||
fullClassName = packageName + "." + className; //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
// in case the type is enclosed, we need to replace the $ with .
|
||||
fullClassName = fullClassName.replaceAll("\\$", "\\."); //$NON-NLS-1$ //$NON-NLS2$
|
||||
|
||||
// now we try to find the file that contains this class and we open it in the editor.
|
||||
IProject project = getProject();
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
|
||||
try {
|
||||
IType result = javaProject.findType(fullClassName);
|
||||
if (result != null) {
|
||||
JavaUI.openInEditor(result);
|
||||
} else {
|
||||
// split the last segment from the fullClassname
|
||||
int index = fullClassName.lastIndexOf('.');
|
||||
if (index != -1) {
|
||||
createNewClass(fullClassName.substring(0, index),
|
||||
fullClassName.substring(index+1));
|
||||
} else {
|
||||
createNewClass(packageName, className);
|
||||
}
|
||||
}
|
||||
} catch (JavaModelException e) {
|
||||
} catch (PartInitException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IProject getProject() {
|
||||
UiElementNode uiNode = getUiParent();
|
||||
AndroidEditor editor = uiNode.getEditor();
|
||||
IEditorInput input = editor.getEditorInput();
|
||||
if (input instanceof IFileEditorInput) {
|
||||
// from the file editor we can get the IFile object, and from it, the IProject.
|
||||
IFile file = ((IFileEditorInput)input).getFile();
|
||||
return file.getProject();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current value of the /manifest/package attribute.
|
||||
* @return the package or an empty string if not found
|
||||
*/
|
||||
private String getManifestPackage() {
|
||||
// get the root uiNode to get the 'package' attribute value.
|
||||
UiElementNode rootNode = getUiParent().getUiRoot();
|
||||
|
||||
Element xmlElement = (Element) rootNode.getXmlNode();
|
||||
|
||||
if (xmlElement != null) {
|
||||
return xmlElement.getAttribute(AndroidManifestDescriptors.PACKAGE_ATTR);
|
||||
}
|
||||
return ""; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes and return the {@link IPackageFragmentRoot}s corresponding to the source folders of
|
||||
* the specified project.
|
||||
* @param project the project
|
||||
* @return an array of IPackageFragmentRoot.
|
||||
*/
|
||||
private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project) {
|
||||
ArrayList<IPackageFragmentRoot> result = new ArrayList<IPackageFragmentRoot>();
|
||||
try {
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
|
||||
for (int i = 0; i < roots.length; i++) {
|
||||
IClasspathEntry entry = roots[i].getRawClasspathEntry();
|
||||
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
|
||||
result.add(roots[i]);
|
||||
}
|
||||
}
|
||||
} catch (JavaModelException e) {
|
||||
}
|
||||
|
||||
return result.toArray(new IPackageFragmentRoot[result.size()]);
|
||||
}
|
||||
|
||||
private void handleNewType(IType type) {
|
||||
Text text = getTextWidget();
|
||||
|
||||
// get the fully qualified name with $ to properly detect the enclosing types.
|
||||
String name = type.getFullyQualifiedName('$');
|
||||
|
||||
String packageValue = getManifestPackage();
|
||||
|
||||
// check if the class doesn't start with the package.
|
||||
if (packageValue.length() > 0 && name.startsWith(packageValue)) {
|
||||
// if it does, we remove the package and the first dot.
|
||||
name = name.substring(packageValue.length() + 1);
|
||||
|
||||
// look for how many segments we have left.
|
||||
// if one, just write it that way.
|
||||
// if more than one, write it with a leading dot.
|
||||
String[] packages = name.split(AndroidConstants.RE_DOT);
|
||||
if (packages.length == 1) {
|
||||
text.setText(name);
|
||||
} else {
|
||||
text.setText("." + name); //$NON-NLS-1$
|
||||
}
|
||||
} else {
|
||||
text.setText(name);
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewClass(String packageName, String className) {
|
||||
// create the wizard page for the class creation, and configure it
|
||||
NewClassWizardPage page = new NewClassWizardPage();
|
||||
|
||||
// set the parent class
|
||||
page.setSuperClass(mReferenceClass, true /* canBeModified */);
|
||||
|
||||
// get the source folders as java elements.
|
||||
IPackageFragmentRoot[] roots = getPackageFragmentRoots(getProject());
|
||||
|
||||
IPackageFragmentRoot currentRoot = null;
|
||||
IPackageFragment currentFragment = null;
|
||||
int packageMatchCount = -1;
|
||||
|
||||
for (IPackageFragmentRoot root : roots) {
|
||||
// Get the java element for the package.
|
||||
// This method is said to always return a IPackageFragment even if the
|
||||
// underlying folder doesn't exist...
|
||||
IPackageFragment fragment = root.getPackageFragment(packageName);
|
||||
if (fragment != null && fragment.exists()) {
|
||||
// we have a perfect match! we use it.
|
||||
currentRoot = root;
|
||||
currentFragment = fragment;
|
||||
packageMatchCount = -1;
|
||||
break;
|
||||
} else {
|
||||
// we don't have a match. we look for the fragment with the best match
|
||||
// (ie the closest parent package we can find)
|
||||
try {
|
||||
IJavaElement[] children;
|
||||
children = root.getChildren();
|
||||
for (IJavaElement child : children) {
|
||||
if (child instanceof IPackageFragment) {
|
||||
fragment = (IPackageFragment)child;
|
||||
if (packageName.startsWith(fragment.getElementName())) {
|
||||
// its a match. get the number of segments
|
||||
String[] segments = fragment.getElementName().split("\\."); //$NON-NLS-1$
|
||||
if (segments.length > packageMatchCount) {
|
||||
packageMatchCount = segments.length;
|
||||
currentFragment = fragment;
|
||||
currentRoot = root;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JavaModelException e) {
|
||||
// Couldn't get the children: we just ignore this package root.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<IPackageFragment> createdFragments = null;
|
||||
|
||||
if (currentRoot != null) {
|
||||
// if we have a perfect match, we set it and we're done.
|
||||
if (packageMatchCount == -1) {
|
||||
page.setPackageFragmentRoot(currentRoot, true /* canBeModified*/);
|
||||
page.setPackageFragment(currentFragment, true /* canBeModified */);
|
||||
} else {
|
||||
// we have a partial match.
|
||||
// create the package. We have to start with the first segment so that we
|
||||
// know what to delete in case of a cancel.
|
||||
try {
|
||||
createdFragments = new ArrayList<IPackageFragment>();
|
||||
|
||||
int totalCount = packageName.split("\\.").length; //$NON-NLS-1$
|
||||
int count = 0;
|
||||
int index = -1;
|
||||
// skip the matching packages
|
||||
while (count < packageMatchCount) {
|
||||
index = packageName.indexOf('.', index+1);
|
||||
count++;
|
||||
}
|
||||
|
||||
// create the rest of the segments, except for the last one as indexOf will
|
||||
// return -1;
|
||||
while (count < totalCount - 1) {
|
||||
index = packageName.indexOf('.', index+1);
|
||||
count++;
|
||||
createdFragments.add(currentRoot.createPackageFragment(
|
||||
packageName.substring(0, index),
|
||||
true /* force*/, new NullProgressMonitor()));
|
||||
}
|
||||
|
||||
// create the last package
|
||||
createdFragments.add(currentRoot.createPackageFragment(
|
||||
packageName, true /* force*/, new NullProgressMonitor()));
|
||||
|
||||
// set the root and fragment in the Wizard page
|
||||
page.setPackageFragmentRoot(currentRoot, true /* canBeModified*/);
|
||||
page.setPackageFragment(createdFragments.get(createdFragments.size()-1),
|
||||
true /* canBeModified */);
|
||||
} catch (JavaModelException e) {
|
||||
// if we can't create the packages, there's a problem. we revert to the default
|
||||
// package
|
||||
for (IPackageFragmentRoot root : roots) {
|
||||
// Get the java element for the package.
|
||||
// This method is said to always return a IPackageFragment even if the
|
||||
// underlying folder doesn't exist...
|
||||
IPackageFragment fragment = root.getPackageFragment(packageName);
|
||||
if (fragment != null && fragment.exists()) {
|
||||
page.setPackageFragmentRoot(root, true /* canBeModified*/);
|
||||
page.setPackageFragment(fragment, true /* canBeModified */);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (roots.length > 0) {
|
||||
// if we haven't found a valid fragment, we set the root to the first source folder.
|
||||
page.setPackageFragmentRoot(roots[0], true /* canBeModified*/);
|
||||
}
|
||||
|
||||
// if we have a starting class name we use it
|
||||
if (className != null) {
|
||||
page.setTypeName(className, true /* canBeModified*/);
|
||||
}
|
||||
|
||||
// create the action that will open it the wizard.
|
||||
OpenNewClassWizardAction action = new OpenNewClassWizardAction();
|
||||
action.setConfiguredWizardPage(page);
|
||||
action.run();
|
||||
IJavaElement element = action.getCreatedElement();
|
||||
|
||||
if (element != null) {
|
||||
if (element.getElementType() == IJavaElement.TYPE) {
|
||||
|
||||
IType type = (IType)element;
|
||||
|
||||
if (mPostCreationAction != null) {
|
||||
mPostCreationAction.processNewType(type);
|
||||
}
|
||||
|
||||
handleNewType(type);
|
||||
}
|
||||
} else {
|
||||
// lets delete the packages we created just for this.
|
||||
// we need to start with the leaf and go up
|
||||
if (createdFragments != null) {
|
||||
try {
|
||||
for (int i = createdFragments.size() - 1 ; i >= 0 ; i--) {
|
||||
createdFragments.get(i).delete(true /* force*/, new NullProgressMonitor());
|
||||
}
|
||||
} catch (JavaModelException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error messages. If message is <code>null</code>, the message is removed.
|
||||
* @param message the message to set, or <code>null</code> to remove the current message
|
||||
* @param textWidget the {@link Text} widget associated to the message.
|
||||
*/
|
||||
private final void setErrorMessage(String message, Text textWidget) {
|
||||
if (message != null) {
|
||||
setHasError(true);
|
||||
getManagedForm().getMessageManager().addMessage(textWidget, message, null /* data */,
|
||||
IMessageProvider.ERROR, textWidget);
|
||||
} else {
|
||||
setHasError(false);
|
||||
getManagedForm().getMessageManager().removeMessage(textWidget, textWidget);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPossibleValues() {
|
||||
// TODO: compute a list of existing classes for content assist completion
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.model;
|
||||
|
||||
import com.android.ide.eclipse.common.AndroidConstants;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.ManifestElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Represents an XML node that can be modified by the user interface in the XML editor.
|
||||
* <p/>
|
||||
* Each tree viewer used in the application page's parts needs to keep a model representing
|
||||
* each underlying node in the tree. This interface represents the base type for such a node.
|
||||
* <p/>
|
||||
* Each node acts as an intermediary model between the actual XML model (the real data support)
|
||||
* and the tree viewers or the corresponding page parts.
|
||||
* <p/>
|
||||
* Element nodes don't contain data per se. Their data is contained in their attributes
|
||||
* as well as their children's attributes, see {@link UiAttributeNode}.
|
||||
* <p/>
|
||||
* The structure of a given {@link UiElementNode} is declared by a corresponding
|
||||
* {@link ElementDescriptor}.
|
||||
*/
|
||||
public final class UiManifestElementNode extends UiElementNode {
|
||||
|
||||
/**
|
||||
* Creates a new {@link UiElementNode} described by a given {@link ElementDescriptor}.
|
||||
*
|
||||
* @param elementDescriptor The {@link ElementDescriptor} for the XML node. Cannot be null.
|
||||
*/
|
||||
public UiManifestElementNode(ManifestElementDescriptor elementDescriptor) {
|
||||
super(elementDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a short string describing the UI node suitable for tree views.
|
||||
* Uses the element's attribute "android:name" if present, or the "android:label" one
|
||||
* followed by the element's name.
|
||||
*
|
||||
* @return A short string describing the UI node suitable for tree views.
|
||||
*/
|
||||
@Override
|
||||
public String getShortDescription() {
|
||||
if (getXmlNode() != null &&
|
||||
getXmlNode() instanceof Element &&
|
||||
getXmlNode().hasAttributes()) {
|
||||
|
||||
// Application and Manifest nodes have a special treatment: they are unique nodes
|
||||
// so we don't bother trying to differentiate their strings and we fall back to
|
||||
// just using the UI name below.
|
||||
ElementDescriptor desc = getDescriptor();
|
||||
if (desc != AndroidManifestDescriptors.MANIFEST_ELEMENT &&
|
||||
desc != AndroidManifestDescriptors.APPLICATION_ELEMENT) {
|
||||
Element elem = (Element) getXmlNode();
|
||||
String attr = elem.getAttributeNS(AndroidConstants.NS_RESOURCES,
|
||||
AndroidManifestDescriptors.ANDROID_NAME_ATTR);
|
||||
if (attr == null || attr.length() == 0) {
|
||||
attr = elem.getAttributeNS(AndroidConstants.NS_RESOURCES,
|
||||
AndroidManifestDescriptors.ANDROID_LABEL_ATTR);
|
||||
}
|
||||
if (attr != null && attr.length() > 0) {
|
||||
return String.format("%1$s (%2$s)", attr, getDescriptor().getUiName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return String.format("%1$s", getDescriptor().getUiName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,319 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.model;
|
||||
|
||||
import com.android.ide.eclipse.editors.AndroidEditor;
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.ui.SectionHelper;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiTextAttributeNode;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.jdt.core.IClasspathEntry;
|
||||
import org.eclipse.jdt.core.IJavaElement;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.IPackageFragment;
|
||||
import org.eclipse.jdt.core.IPackageFragmentRoot;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
import org.eclipse.jdt.ui.JavaUI;
|
||||
import org.eclipse.jdt.ui.actions.OpenNewPackageWizardAction;
|
||||
import org.eclipse.jdt.ui.actions.ShowInPackageViewAction;
|
||||
import org.eclipse.jface.dialogs.IMessageProvider;
|
||||
import org.eclipse.jface.viewers.StructuredSelection;
|
||||
import org.eclipse.jface.window.Window;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.DisposeEvent;
|
||||
import org.eclipse.swt.events.DisposeListener;
|
||||
import org.eclipse.swt.events.ModifyEvent;
|
||||
import org.eclipse.swt.events.ModifyListener;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Text;
|
||||
import org.eclipse.ui.IEditorInput;
|
||||
import org.eclipse.ui.IFileEditorInput;
|
||||
import org.eclipse.ui.IWorkbenchPartSite;
|
||||
import org.eclipse.ui.dialogs.SelectionDialog;
|
||||
import org.eclipse.ui.forms.IManagedForm;
|
||||
import org.eclipse.ui.forms.events.HyperlinkAdapter;
|
||||
import org.eclipse.ui.forms.events.HyperlinkEvent;
|
||||
import org.eclipse.ui.forms.widgets.FormText;
|
||||
import org.eclipse.ui.forms.widgets.FormToolkit;
|
||||
import org.eclipse.ui.forms.widgets.TableWrapData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Represents an XML attribute for a package, that can be modified using a simple text field or
|
||||
* a dialog to choose an existing package. Also, there's a link to create a new package.
|
||||
* <p/>
|
||||
* See {@link UiTextAttributeNode} for more information.
|
||||
*/
|
||||
public class UiPackageAttributeNode extends UiTextAttributeNode {
|
||||
|
||||
/**
|
||||
* Creates a {@link UiPackageAttributeNode} object that will display ui to select or create
|
||||
* a package.
|
||||
* @param attributeDescriptor the {@link AttributeDescriptor} object linked to the Ui Node.
|
||||
*/
|
||||
public UiPackageAttributeNode(AttributeDescriptor attributeDescriptor, UiElementNode uiParent) {
|
||||
super(attributeDescriptor, uiParent);
|
||||
}
|
||||
|
||||
/* (non-java doc)
|
||||
* Creates a label widget and an associated text field.
|
||||
* <p/>
|
||||
* As most other parts of the android manifest editor, this assumes the
|
||||
* parent uses a table layout with 2 columns.
|
||||
*/
|
||||
@Override
|
||||
public void createUiControl(final Composite parent, final IManagedForm managedForm) {
|
||||
setManagedForm(managedForm);
|
||||
FormToolkit toolkit = managedForm.getToolkit();
|
||||
TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor();
|
||||
|
||||
StringBuilder label = new StringBuilder();
|
||||
label.append("<form><p><a href='unused'>"); //$NON-NLS-1$
|
||||
label.append(desc.getUiName());
|
||||
label.append("</a></p></form>"); //$NON-NLS-1$
|
||||
FormText formText = SectionHelper.createFormText(parent, toolkit, true /* isHtml */,
|
||||
label.toString(), true /* setupLayoutData */);
|
||||
formText.addHyperlinkListener(new HyperlinkAdapter() {
|
||||
@Override
|
||||
public void linkActivated(HyperlinkEvent e) {
|
||||
super.linkActivated(e);
|
||||
doLabelClick();
|
||||
}
|
||||
});
|
||||
formText.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE));
|
||||
SectionHelper.addControlTooltip(formText, desc.getTooltip());
|
||||
|
||||
Composite composite = toolkit.createComposite(parent);
|
||||
composite.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE));
|
||||
GridLayout gl = new GridLayout(2, false);
|
||||
gl.marginHeight = gl.marginWidth = 0;
|
||||
composite.setLayout(gl);
|
||||
// Fixes missing text borders under GTK... also requires adding a 1-pixel margin
|
||||
// for the text field below
|
||||
toolkit.paintBordersFor(composite);
|
||||
|
||||
final Text text = toolkit.createText(composite, getCurrentValue());
|
||||
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
|
||||
gd.horizontalIndent = 1; // Needed by the fixed composite borders under GTK
|
||||
text.setLayoutData(gd);
|
||||
|
||||
setTextWidget(text);
|
||||
|
||||
Button browseButton = toolkit.createButton(composite, "Browse...", SWT.PUSH);
|
||||
|
||||
browseButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
super.widgetSelected(e);
|
||||
doBrowseClick();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/* (non-java doc)
|
||||
* Adds a validator to the text field that calls managedForm.getMessageManager().
|
||||
*/
|
||||
@Override
|
||||
protected void onAddValidators(final Text text) {
|
||||
ModifyListener listener = new ModifyListener() {
|
||||
public void modifyText(ModifyEvent e) {
|
||||
String package_name = text.getText();
|
||||
if (package_name.indexOf('.') < 1) {
|
||||
getManagedForm().getMessageManager().addMessage(text,
|
||||
"Package name should contain at least two identifiers.",
|
||||
null /* data */, IMessageProvider.ERROR, text);
|
||||
} else {
|
||||
getManagedForm().getMessageManager().removeMessage(text, text);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
text.addModifyListener(listener);
|
||||
|
||||
// Make sure the validator removes its message(s) when the widget is disposed
|
||||
text.addDisposeListener(new DisposeListener() {
|
||||
public void widgetDisposed(DisposeEvent e) {
|
||||
getManagedForm().getMessageManager().removeMessage(text, text);
|
||||
}
|
||||
});
|
||||
|
||||
// Finally call the validator once to make sure the initial value is processed
|
||||
listener.modifyText(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles response to the Browse button by creating a Package dialog.
|
||||
* */
|
||||
private void doBrowseClick() {
|
||||
Text text = getTextWidget();
|
||||
|
||||
// we need to get the project of the manifest.
|
||||
IProject project = getProject();
|
||||
if (project != null) {
|
||||
|
||||
try {
|
||||
SelectionDialog dlg = JavaUI.createPackageDialog(text.getShell(),
|
||||
JavaCore.create(project), 0);
|
||||
dlg.setTitle("Select Android Package");
|
||||
dlg.setMessage("Select the package for the Android project.");
|
||||
dlg.setDefaultImage(EditorsPlugin.getAndroidLogo());
|
||||
|
||||
if (dlg.open() == Window.OK) {
|
||||
Object[] results = dlg.getResult();
|
||||
if (results.length == 1) {
|
||||
setPackageTextField((IPackageFragment)results[0]);
|
||||
}
|
||||
}
|
||||
} catch (JavaModelException e1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles response to the Label hyper link being activated.
|
||||
*/
|
||||
private void doLabelClick() {
|
||||
// get the current package name
|
||||
String package_name = getTextWidget().getText().trim();
|
||||
|
||||
if (package_name.length() == 0) {
|
||||
createNewPackage();
|
||||
} else {
|
||||
// Try to select the package in the Package Explorer for the current
|
||||
// project and the current editor's site.
|
||||
|
||||
IProject project = getProject();
|
||||
if (project == null) {
|
||||
EditorsPlugin.log(IStatus.ERROR, "Failed to get project for UiPackageAttribute"); //$NON-NLS-1$
|
||||
return;
|
||||
}
|
||||
|
||||
IWorkbenchPartSite site = getUiParent().getEditor().getSite();
|
||||
if (site == null) {
|
||||
EditorsPlugin.log(IStatus.ERROR, "Failed to get editor site for UiPackageAttribute"); //$NON-NLS-1$
|
||||
return;
|
||||
}
|
||||
|
||||
for (IPackageFragmentRoot root : getPackageFragmentRoots(project)) {
|
||||
IPackageFragment fragment = root.getPackageFragment(package_name);
|
||||
if (fragment != null && fragment.exists()) {
|
||||
ShowInPackageViewAction action = new ShowInPackageViewAction(site);
|
||||
action.run(fragment);
|
||||
// This action's run() doesn't provide the status (although internally it could)
|
||||
// so we just assume it worked.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that returns the project for the current file being edited.
|
||||
*
|
||||
* @return The IProject for the current file being edited or null.
|
||||
*/
|
||||
private IProject getProject() {
|
||||
UiElementNode uiNode = getUiParent();
|
||||
AndroidEditor editor = uiNode.getEditor();
|
||||
IEditorInput input = editor.getEditorInput();
|
||||
if (input instanceof IFileEditorInput) {
|
||||
// from the file editor we can get the IFile object, and from it, the IProject.
|
||||
IFile file = ((IFileEditorInput)input).getFile();
|
||||
return file.getProject();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that computes and returns the list of {@link IPackageFragmentRoot}
|
||||
* corresponding to the source folder of the specified project.
|
||||
*
|
||||
* @param project the project
|
||||
* @return an array of IPackageFragmentRoot. Can be empty but not null.
|
||||
*/
|
||||
private IPackageFragmentRoot[] getPackageFragmentRoots(IProject project) {
|
||||
ArrayList<IPackageFragmentRoot> result = new ArrayList<IPackageFragmentRoot>();
|
||||
try {
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
|
||||
for (int i = 0; i < roots.length; i++) {
|
||||
IClasspathEntry entry = roots[i].getRawClasspathEntry();
|
||||
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
|
||||
result.add(roots[i]);
|
||||
}
|
||||
}
|
||||
} catch (JavaModelException e) {
|
||||
}
|
||||
|
||||
return result.toArray(new IPackageFragmentRoot[result.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that sets the package's text field to the package fragment's name.
|
||||
* */
|
||||
private void setPackageTextField(IPackageFragment type) {
|
||||
Text text = getTextWidget();
|
||||
|
||||
String name = type.getElementName();
|
||||
|
||||
text.setText(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Displays and handles a "Create Package Wizard".
|
||||
*
|
||||
* This is invoked by doLabelClick() when clicking on the hyperlink label with an
|
||||
* empty package text field.
|
||||
*/
|
||||
private void createNewPackage() {
|
||||
OpenNewPackageWizardAction action = new OpenNewPackageWizardAction();
|
||||
|
||||
IProject project = getProject();
|
||||
action.setSelection(new StructuredSelection(project));
|
||||
action.run();
|
||||
|
||||
IJavaElement element = action.getCreatedElement();
|
||||
if (element != null &&
|
||||
element.exists() &&
|
||||
element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
|
||||
setPackageTextField((IPackageFragment) element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPossibleValues() {
|
||||
// TODO: compute a list of existing packages for content assist completion
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.pages;
|
||||
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.descriptors.XmlnsAttributeDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
|
||||
import com.android.ide.eclipse.editors.ui.UiElementPart;
|
||||
import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.ui.forms.IManagedForm;
|
||||
import org.eclipse.ui.forms.widgets.FormToolkit;
|
||||
import org.eclipse.ui.forms.widgets.Section;
|
||||
|
||||
/**
|
||||
* Application's attributes section part for Application page.
|
||||
* <p/>
|
||||
* This part is displayed at the top of the application page and displays all the possible
|
||||
* attributes of an application node in the AndroidManifest (icon, class name, label, etc.)
|
||||
*/
|
||||
final class ApplicationAttributesPart extends UiElementPart {
|
||||
|
||||
/** Listen to changes to the UI node for <application> and updates the UI */
|
||||
private AppNodeUpdateListener mAppNodeUpdateListener;
|
||||
/** ManagedForm needed to create the UI controls */
|
||||
private IManagedForm mManagedForm;
|
||||
|
||||
public ApplicationAttributesPart(Composite body, FormToolkit toolkit, ManifestEditor editor,
|
||||
UiElementNode applicationUiNode) {
|
||||
super(body, toolkit, editor, applicationUiNode,
|
||||
"Application Attributes", // section title
|
||||
"Defines the attributes specific to the application.", // section description
|
||||
Section.TWISTIE | Section.EXPANDED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes and refreshes the Application UI node handle by the this part.
|
||||
*/
|
||||
@Override
|
||||
public void setUiElementNode(UiElementNode uiElementNode) {
|
||||
super.setUiElementNode(uiElementNode);
|
||||
|
||||
createUiAttributes(mManagedForm);
|
||||
}
|
||||
|
||||
/* (non-java doc)
|
||||
* Create the controls to edit the attributes for the given ElementDescriptor.
|
||||
* <p/>
|
||||
* This MUST not be called by the constructor. Instead it must be called from
|
||||
* <code>initialize</code> (i.e. right after the form part is added to the managed form.)
|
||||
* <p/>
|
||||
* Derived classes can override this if necessary.
|
||||
*
|
||||
* @param managedForm The owner managed form
|
||||
*/
|
||||
@Override
|
||||
protected void createFormControls(final IManagedForm managedForm) {
|
||||
mManagedForm = managedForm;
|
||||
setTable(createTableLayout(managedForm.getToolkit(), 4 /* numColumns */));
|
||||
|
||||
mAppNodeUpdateListener = new AppNodeUpdateListener();
|
||||
getUiElementNode().addUpdateListener(mAppNodeUpdateListener);
|
||||
|
||||
createUiAttributes(mManagedForm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (getUiElementNode() != null && mAppNodeUpdateListener != null) {
|
||||
getUiElementNode().removeUpdateListener(mAppNodeUpdateListener);
|
||||
mAppNodeUpdateListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createUiAttributes(IManagedForm managedForm) {
|
||||
Composite table = getTable();
|
||||
if (table == null || managedForm == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any old UI controls
|
||||
for (Control c : table.getChildren()) {
|
||||
c.dispose();
|
||||
}
|
||||
|
||||
UiElementNode uiElementNode = getUiElementNode();
|
||||
AttributeDescriptor[] attr_desc_list = uiElementNode.getAttributeDescriptors();
|
||||
|
||||
// Display the attributes in 2 columns:
|
||||
// attr 0 | attr 4
|
||||
// attr 1 | attr 5
|
||||
// attr 2 | attr 6
|
||||
// attr 3 | attr 7
|
||||
// that is we have to fill the grid in order 0, 4, 1, 5, 2, 6, 3, 7
|
||||
// thus index = i/2 + (i is odd * n/2)
|
||||
int n = attr_desc_list.length;
|
||||
int n2 = (int) Math.ceil(n / 2.0);
|
||||
for (int i = 0; i < n; i++) {
|
||||
AttributeDescriptor attr_desc = attr_desc_list[i / 2 + (i & 1) * n2];
|
||||
if (attr_desc instanceof XmlnsAttributeDescriptor) {
|
||||
// Do not show hidden attributes
|
||||
continue;
|
||||
}
|
||||
|
||||
UiAttributeNode ui_attr = uiElementNode.findUiAttribute(attr_desc);
|
||||
if (ui_attr != null) {
|
||||
ui_attr.createUiControl(table, managedForm);
|
||||
} else {
|
||||
// The XML has an extra attribute which wasn't declared in
|
||||
// AndroidManifestDescriptors. This is not a problem, we just ignore it.
|
||||
EditorsPlugin.log(IStatus.WARNING,
|
||||
"Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$
|
||||
attr_desc.getXmlLocalName(),
|
||||
uiElementNode.getDescriptor().getXmlName());
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
createLabel(table, managedForm.getToolkit(),
|
||||
"No attributes to display, waiting for SDK to finish loading...",
|
||||
null /* tooltip */ );
|
||||
}
|
||||
|
||||
// Initialize the enabled/disabled state
|
||||
if (mAppNodeUpdateListener != null) {
|
||||
mAppNodeUpdateListener.uiElementNodeUpdated(uiElementNode, null /* state, not used */);
|
||||
}
|
||||
|
||||
// Tell the section that the layout has changed.
|
||||
layoutChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* This listener synchronizes the UI with the actual presence of the application XML node.
|
||||
*/
|
||||
private class AppNodeUpdateListener implements IUiUpdateListener {
|
||||
public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
|
||||
// The UiElementNode for the application XML node always exists, even
|
||||
// if there is no corresponding XML node in the XML file.
|
||||
//
|
||||
// We enable the UI here if the XML node is not null.
|
||||
Composite table = getTable();
|
||||
boolean exists = (ui_node.getXmlNode() != null);
|
||||
if (table != null && table.getEnabled() != exists) {
|
||||
table.setEnabled(exists);
|
||||
for (Control c : table.getChildren()) {
|
||||
c.setEnabled(exists);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.pages;
|
||||
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
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.FormToolkit;
|
||||
import org.eclipse.ui.forms.widgets.ScrolledForm;
|
||||
|
||||
|
||||
/**
|
||||
* Page for "Application" settings, part of the AndroidManifest form editor.
|
||||
* <p/>
|
||||
* Useful reference:
|
||||
* <a href="http://www.eclipse.org/articles/Article-Forms/article.html">
|
||||
* http://www.eclipse.org/articles/Article-Forms/article.html</a>
|
||||
*/
|
||||
public final class ApplicationPage extends FormPage {
|
||||
/** Page id used for switching tabs programmatically */
|
||||
public final static String PAGE_ID = "application_page"; //$NON-NLS-1$
|
||||
|
||||
/** Container editor */
|
||||
ManifestEditor mEditor;
|
||||
/** The Application Toogle part */
|
||||
private ApplicationToggle mTooglePart;
|
||||
/** The Application Attributes part */
|
||||
private ApplicationAttributesPart mAttrPart;
|
||||
/** The tree view block */
|
||||
private UiTreeBlock mTreeBlock;
|
||||
|
||||
public ApplicationPage(ManifestEditor editor) {
|
||||
super(editor, PAGE_ID, "Application"); // tab's label, 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 Manifest Application");
|
||||
form.setImage(EditorsPlugin.getAndroidLogo());
|
||||
|
||||
UiElementNode appUiNode = getUiApplicationNode();
|
||||
|
||||
Composite body = form.getBody();
|
||||
FormToolkit toolkit = managedForm.getToolkit();
|
||||
|
||||
// We usually prefer to have a ColumnLayout here. However
|
||||
// MasterDetailsBlock.createContent() below will reset the body's layout to a grid layout.
|
||||
mTooglePart = new ApplicationToggle(body, toolkit, mEditor, appUiNode);
|
||||
mTooglePart.getSection().setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
|
||||
managedForm.addPart(mTooglePart);
|
||||
mAttrPart = new ApplicationAttributesPart(body, toolkit, mEditor, appUiNode);
|
||||
mAttrPart.getSection().setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
|
||||
managedForm.addPart(mAttrPart);
|
||||
|
||||
mTreeBlock = new UiTreeBlock(mEditor, appUiNode,
|
||||
false /* autoCreateRoot */,
|
||||
null /* element filters */,
|
||||
"Application Nodes",
|
||||
"List of all elements in the application");
|
||||
mTreeBlock.createContent(managedForm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the application UI node. Since this is a mandatory node, it *always*
|
||||
* exists, even if there is no matching XML node.
|
||||
*/
|
||||
private UiElementNode getUiApplicationNode() {
|
||||
ElementDescriptor desc = AndroidManifestDescriptors.APPLICATION_ELEMENT;
|
||||
return mEditor.getUiRootNode().findUiChildNode(desc.getXmlName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes and refreshes the Application UI node handled by the sub parts.
|
||||
*/
|
||||
public void refreshUiApplicationNode() {
|
||||
UiElementNode appUiNode = getUiApplicationNode();
|
||||
if (mTooglePart != null) {
|
||||
mTooglePart.setUiElementNode(appUiNode);
|
||||
}
|
||||
if (mAttrPart != null) {
|
||||
mAttrPart.setUiElementNode(appUiNode);
|
||||
}
|
||||
if (mTreeBlock != null) {
|
||||
mTreeBlock.changeRootAndDescriptors(appUiNode,
|
||||
null /* element filters */,
|
||||
true /* refresh */);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.pages;
|
||||
|
||||
import com.android.ide.eclipse.common.resources.FrameworkResourceManager;
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils;
|
||||
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
|
||||
import com.android.ide.eclipse.editors.ui.UiElementPart;
|
||||
import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.IUiUpdateListener.UiUpdateState;
|
||||
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.ui.forms.IManagedForm;
|
||||
import org.eclipse.ui.forms.widgets.FormText;
|
||||
import org.eclipse.ui.forms.widgets.FormToolkit;
|
||||
import org.eclipse.ui.forms.widgets.Section;
|
||||
import org.eclipse.ui.forms.widgets.TableWrapData;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.Text;
|
||||
|
||||
/**
|
||||
* Appllication Toogle section part for application page.
|
||||
*/
|
||||
final class ApplicationToggle extends UiElementPart {
|
||||
|
||||
/** Checkbox indicating whether an application node is present */
|
||||
private Button mCheckbox;
|
||||
/** Listen to changes to the UI node for <application> and updates the checkbox */
|
||||
private AppNodeUpdateListener mAppNodeUpdateListener;
|
||||
/** Internal flag to know where we're programmatically modifying the checkbox and we want to
|
||||
* avoid triggering the checkbox's callback. */
|
||||
public boolean mInternalModification;
|
||||
private FormText mTooltipFormText;
|
||||
|
||||
public ApplicationToggle(Composite body, FormToolkit toolkit, ManifestEditor editor,
|
||||
UiElementNode applicationUiNode) {
|
||||
super(body, toolkit, editor, applicationUiNode,
|
||||
"Application Toggle",
|
||||
null, /* description */
|
||||
Section.TWISTIE | Section.EXPANDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (getUiElementNode() != null && mAppNodeUpdateListener != null) {
|
||||
getUiElementNode().removeUpdateListener(mAppNodeUpdateListener);
|
||||
mAppNodeUpdateListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes and refreshes the Application UI node handle by the this part.
|
||||
*/
|
||||
@Override
|
||||
public void setUiElementNode(UiElementNode uiElementNode) {
|
||||
super.setUiElementNode(uiElementNode);
|
||||
|
||||
updateTooltip();
|
||||
|
||||
// Set the state of the checkbox
|
||||
mAppNodeUpdateListener.uiElementNodeUpdated(getUiElementNode(),
|
||||
UiUpdateState.CHILDREN_CHANGED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the controls to edit the attributes for the given ElementDescriptor.
|
||||
* <p/>
|
||||
* This MUST not be called by the constructor. Instead it must be called from
|
||||
* <code>initialize</code> (i.e. right after the form part is added to the managed form.)
|
||||
*
|
||||
* @param managedForm The owner managed form
|
||||
*/
|
||||
@Override
|
||||
protected void createFormControls(IManagedForm managedForm) {
|
||||
FormToolkit toolkit = managedForm.getToolkit();
|
||||
Composite table = createTableLayout(toolkit, 1 /* numColumns */);
|
||||
|
||||
mTooltipFormText = createFormText(table, toolkit, true, "<form></form>",
|
||||
false /* setupLayoutData */);
|
||||
updateTooltip();
|
||||
|
||||
mCheckbox = toolkit.createButton(table,
|
||||
"Define an <application> tag in the AndroidManifest.xml",
|
||||
SWT.CHECK);
|
||||
mCheckbox.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP));
|
||||
mCheckbox.setSelection(false);
|
||||
mCheckbox.addSelectionListener(new CheckboxSelectionListener());
|
||||
|
||||
mAppNodeUpdateListener = new AppNodeUpdateListener();
|
||||
getUiElementNode().addUpdateListener(mAppNodeUpdateListener);
|
||||
|
||||
// Initialize the state of the checkbox
|
||||
mAppNodeUpdateListener.uiElementNodeUpdated(getUiElementNode(),
|
||||
UiUpdateState.CHILDREN_CHANGED);
|
||||
|
||||
// Tell the section that the layout has changed.
|
||||
layoutChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the application tooltip in the form text.
|
||||
* If there is no tooltip, the form text is hidden.
|
||||
*/
|
||||
private void updateTooltip() {
|
||||
boolean isVisible = false;
|
||||
|
||||
String tooltip = getUiElementNode().getDescriptor().getTooltip();
|
||||
if (tooltip != null) {
|
||||
tooltip = DescriptorsUtils.formatFormText(tooltip,
|
||||
getUiElementNode().getDescriptor(),
|
||||
FrameworkResourceManager.getInstance().getDocumentationBaseUrl());
|
||||
|
||||
mTooltipFormText.setText(tooltip, true /* parseTags */, true /* expandURLs */);
|
||||
mTooltipFormText.setImage(DescriptorsUtils.IMAGE_KEY, EditorsPlugin.getAndroidLogo());
|
||||
mTooltipFormText.addHyperlinkListener(getEditor().createHyperlinkListener());
|
||||
isVisible = true;
|
||||
}
|
||||
|
||||
mTooltipFormText.setVisible(isVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
* This listener synchronizes the XML application node when the checkbox
|
||||
* is changed by the user.
|
||||
*/
|
||||
private class CheckboxSelectionListener extends SelectionAdapter {
|
||||
private Node mUndoXmlNode;
|
||||
private Node mUndoXmlParent;
|
||||
private Node mUndoXmlNextNode;
|
||||
private Node mUndoXmlNextElement;
|
||||
private Document mUndoXmlDocument;
|
||||
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
super.widgetSelected(e);
|
||||
if (!mInternalModification && getUiElementNode() != null) {
|
||||
getUiElementNode().getEditor().editXmlModel(new Runnable() {
|
||||
public void run() {
|
||||
if (mCheckbox.getSelection()) {
|
||||
// The user wants an <application> node. Either restore a previous one
|
||||
// or create a full new one.
|
||||
boolean create = true;
|
||||
if (mUndoXmlNode != null) {
|
||||
create = !restoreApplicationNode();
|
||||
}
|
||||
if (create) {
|
||||
getUiElementNode().createXmlNode();
|
||||
}
|
||||
} else {
|
||||
// Users no longer wants the <application> node.
|
||||
removeApplicationNode();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a previously "saved" application node.
|
||||
*
|
||||
* @return True if the node could be restored, false otherwise.
|
||||
*/
|
||||
private boolean restoreApplicationNode() {
|
||||
if (mUndoXmlDocument == null || mUndoXmlNode == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate node references...
|
||||
mUndoXmlParent = validateNode(mUndoXmlDocument, mUndoXmlParent);
|
||||
mUndoXmlNextNode = validateNode(mUndoXmlDocument, mUndoXmlNextNode);
|
||||
mUndoXmlNextElement = validateNode(mUndoXmlDocument, mUndoXmlNextElement);
|
||||
|
||||
if (mUndoXmlParent == null){
|
||||
// If the parent node doesn't exist, try to find a new manifest node.
|
||||
// If it doesn't exist, create it.
|
||||
mUndoXmlParent = getUiElementNode().getUiParent().prepareCommit();
|
||||
mUndoXmlNextNode = null;
|
||||
mUndoXmlNextElement = null;
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
if (mUndoXmlParent != null) {
|
||||
// If the parent is still around, reuse the same node.
|
||||
|
||||
// Ideally we want to insert the node before what used to be its next sibling.
|
||||
// If that's not possible, we try to insert it before its next sibling element.
|
||||
// If that's not possible either, it will be inserted at the end of the parent's.
|
||||
Node next = mUndoXmlNextNode;
|
||||
if (next == null) {
|
||||
next = mUndoXmlNextElement;
|
||||
}
|
||||
mUndoXmlParent.insertBefore(mUndoXmlNode, next);
|
||||
if (next == null) {
|
||||
Text sep = mUndoXmlDocument.createTextNode("\n"); //$NON-NLS-1$
|
||||
mUndoXmlParent.insertBefore(sep, null); // insert separator before end tag
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
|
||||
// Remove internal references to avoid using them twice
|
||||
mUndoXmlParent = null;
|
||||
mUndoXmlNextNode = null;
|
||||
mUndoXmlNextElement = null;
|
||||
mUndoXmlNode = null;
|
||||
mUndoXmlDocument = null;
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the given xml_node is still either the root node or one of its
|
||||
* direct descendants.
|
||||
*
|
||||
* @param root_node The root of the node hierarchy to examine.
|
||||
* @param xml_node The XML node to find.
|
||||
* @return Returns xml_node if it is, otherwise returns null.
|
||||
*/
|
||||
private Node validateNode(Node root_node, Node xml_node) {
|
||||
if (root_node == xml_node) {
|
||||
return xml_node;
|
||||
} else {
|
||||
for (Node node = root_node.getFirstChild(); node != null;
|
||||
node = node.getNextSibling()) {
|
||||
if (root_node == xml_node || validateNode(node, xml_node) != null) {
|
||||
return xml_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the <application> node from the hierarchy.
|
||||
* Before doing that, we try to remember where it was so that we can put it back
|
||||
* in the same place.
|
||||
*/
|
||||
private void removeApplicationNode() {
|
||||
// Make sure the node actually exists...
|
||||
Node xml_node = getUiElementNode().getXmlNode();
|
||||
if (xml_node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save its parent, next sibling and next element
|
||||
mUndoXmlDocument = xml_node.getOwnerDocument();
|
||||
mUndoXmlParent = xml_node.getParentNode();
|
||||
mUndoXmlNextNode = xml_node.getNextSibling();
|
||||
mUndoXmlNextElement = mUndoXmlNextNode;
|
||||
while (mUndoXmlNextElement != null &&
|
||||
mUndoXmlNextElement.getNodeType() != Node.ELEMENT_NODE) {
|
||||
mUndoXmlNextElement = mUndoXmlNextElement.getNextSibling();
|
||||
}
|
||||
|
||||
// Actually remove the node from the hierarchy and keep it here.
|
||||
// The returned node looses its parents/siblings pointers.
|
||||
mUndoXmlNode = getUiElementNode().deleteXmlNode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This listener synchronizes the UI (i.e. the checkbox) with the
|
||||
* actual presence of the application XML node.
|
||||
*/
|
||||
private class AppNodeUpdateListener implements IUiUpdateListener {
|
||||
public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
|
||||
// The UiElementNode for the application XML node always exists, even
|
||||
// if there is no corresponding XML node in the XML file.
|
||||
//
|
||||
// To update the checkbox to reflect the actual state, we just need
|
||||
// to check if the XML node is null.
|
||||
try {
|
||||
mInternalModification = true;
|
||||
boolean exists = ui_node.getXmlNode() != null;
|
||||
if (mCheckbox.getSelection() != exists) {
|
||||
mCheckbox.setSelection(exists);
|
||||
}
|
||||
} finally {
|
||||
mInternalModification = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.pages;
|
||||
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.ui.forms.IManagedForm;
|
||||
import org.eclipse.ui.forms.editor.FormPage;
|
||||
import org.eclipse.ui.forms.widgets.ScrolledForm;
|
||||
|
||||
/**
|
||||
* Page for instrumentation settings, part of the AndroidManifest form editor.
|
||||
*/
|
||||
public final class InstrumentationPage extends FormPage {
|
||||
/** Page id used for switching tabs programmatically */
|
||||
public final static String PAGE_ID = "instrumentation_page"; //$NON-NLS-1$
|
||||
|
||||
/** Container editor */
|
||||
ManifestEditor mEditor;
|
||||
|
||||
public InstrumentationPage(ManifestEditor editor) {
|
||||
super(editor, PAGE_ID, "Instrumentation"); // tab's label, 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 Manifest Instrumentation");
|
||||
form.setImage(EditorsPlugin.getAndroidLogo());
|
||||
|
||||
UiElementNode manifest = mEditor.getUiRootNode();
|
||||
UiTreeBlock block = new UiTreeBlock(mEditor, manifest,
|
||||
true /* autoCreateRoot */,
|
||||
new ElementDescriptor[] { AndroidManifestDescriptors.INTRUMENTATION_ELEMENT },
|
||||
"Instrumentation",
|
||||
"List of instrumentations defined in the manifest");
|
||||
block.createContent(managedForm);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.pages;
|
||||
|
||||
import com.android.ide.eclipse.common.project.ExportHelper;
|
||||
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
|
||||
import com.android.ide.eclipse.editors.ui.SectionHelper.ManifestSectionPart;
|
||||
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.ui.IEditorInput;
|
||||
import org.eclipse.ui.forms.events.HyperlinkAdapter;
|
||||
import org.eclipse.ui.forms.events.HyperlinkEvent;
|
||||
import org.eclipse.ui.forms.widgets.FormText;
|
||||
import org.eclipse.ui.forms.widgets.FormToolkit;
|
||||
import org.eclipse.ui.forms.widgets.Section;
|
||||
import org.eclipse.ui.part.FileEditorInput;
|
||||
|
||||
/**
|
||||
* Export section part for overview page.
|
||||
*/
|
||||
final class OverviewExportPart extends ManifestSectionPart {
|
||||
|
||||
private final OverviewPage mOverviewPage;
|
||||
|
||||
public OverviewExportPart(OverviewPage overviewPage, Composite body, FormToolkit toolkit, ManifestEditor editor) {
|
||||
super(body, toolkit, Section.TWISTIE | Section.EXPANDED, true /* description */);
|
||||
mOverviewPage = overviewPage;
|
||||
Section section = getSection();
|
||||
section.setText("Exporting");
|
||||
section.setDescription("To export the application for distribution, you have the following options:");
|
||||
|
||||
Composite table = createTableLayout(toolkit, 2 /* numColumns */);
|
||||
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append("<form><li><a href=\"wizard\">"); //$NON-NLS-1$
|
||||
buf.append("Use the export wizard");
|
||||
buf.append("</a></li><li><a href=\"manual\">"); //$NON-NLS-1$
|
||||
buf.append("Export an unsigned apk");
|
||||
buf.append("</a>"); //$NON-NLS-1$
|
||||
buf.append(" and sign it manually");
|
||||
buf.append("</li></form>"); //$NON-NLS-1$
|
||||
|
||||
FormText text = createFormText(table, toolkit, true, buf.toString(),
|
||||
false /* setupLayoutData */);
|
||||
text.addHyperlinkListener(new HyperlinkAdapter() {
|
||||
@Override
|
||||
public void linkActivated(HyperlinkEvent e) {
|
||||
// get the project from the editor
|
||||
IEditorInput input = mOverviewPage.mEditor.getEditorInput();
|
||||
if (input instanceof FileEditorInput) {
|
||||
FileEditorInput fileInput = (FileEditorInput)input;
|
||||
IFile file = fileInput.getFile();
|
||||
IProject project = file.getProject();
|
||||
|
||||
if ("manual".equals(e.data)) { //$NON-NLS-1$
|
||||
// now we can export an unsigned apk for the project.
|
||||
ExportHelper.exportProject(project);
|
||||
} else {
|
||||
// call the export wizard
|
||||
ExportHelper.startExportWizard(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
layoutChanged();
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.pages;
|
||||
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.ui.UiElementPart;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.ui.forms.IManagedForm;
|
||||
import org.eclipse.ui.forms.widgets.FormToolkit;
|
||||
import org.eclipse.ui.forms.widgets.Section;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
/**
|
||||
* Generic info section part for overview page
|
||||
*/
|
||||
final class OverviewInfoPart extends UiElementPart {
|
||||
|
||||
private IManagedForm mManagedForm;
|
||||
|
||||
public OverviewInfoPart(Composite body, FormToolkit toolkit, ManifestEditor editor) {
|
||||
super(body, toolkit, editor,
|
||||
null, // uiElementNode
|
||||
"General Information", // section title
|
||||
"Defines general information about the AndroidManifest.xml", // section description
|
||||
Section.TWISTIE | Section.EXPANDED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the UiElementNode that this part will edit. The node must exist
|
||||
* and can't be null, by design, because it's a mandatory node.
|
||||
*/
|
||||
private static UiElementNode getManifestUiNode(ManifestEditor editor) {
|
||||
ElementDescriptor desc = AndroidManifestDescriptors.MANIFEST_ELEMENT;
|
||||
if (editor.getUiRootNode().getDescriptor() == desc) {
|
||||
return editor.getUiRootNode();
|
||||
} else {
|
||||
return editor.getUiRootNode().findUiChildNode(desc.getXmlName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the uses-sdk UI node. Since this is a mandatory node, it *always*
|
||||
* exists, even if there is no matching XML node.
|
||||
*/
|
||||
private UiElementNode getUsesSdkUiNode(ManifestEditor editor) {
|
||||
ElementDescriptor desc = AndroidManifestDescriptors.USES_SDK_ELEMENT;
|
||||
return editor.getUiRootNode().findUiChildNode(desc.getXmlName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden in order to capture the current managed form.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void createFormControls(final IManagedForm managedForm) {
|
||||
mManagedForm = managedForm;
|
||||
super.createFormControls(managedForm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any existing Attribute UI widgets and recreate them if the SDK has changed.
|
||||
* <p/>
|
||||
* This is called by {@link OverviewPage#refreshUiApplicationNode()} when the
|
||||
* SDK has changed.
|
||||
*/
|
||||
public void onSdkChanged() {
|
||||
createUiAttributes(mManagedForm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to add the description and the ui attributes of both the
|
||||
* manifest and uses-sdk UI nodes.
|
||||
* <p/>
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void fillTable(Composite table, IManagedForm managedForm) {
|
||||
int n = 0;
|
||||
|
||||
UiElementNode uiNode = getManifestUiNode(getEditor());
|
||||
n += insertUiAttributes(uiNode, table, managedForm);
|
||||
|
||||
uiNode = getUsesSdkUiNode(getEditor());
|
||||
n += insertUiAttributes(uiNode, table, managedForm);
|
||||
|
||||
if (n == 0) {
|
||||
createLabel(table, managedForm.getToolkit(),
|
||||
"No attributes to display, waiting for SDK to finish loading...",
|
||||
null /* tooltip */ );
|
||||
}
|
||||
|
||||
layoutChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to tests whether either the manifest or uses-sdk nodes parts are dirty.
|
||||
* <p/>
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return <code>true</code> if the part is dirty, <code>false</code>
|
||||
* otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
boolean dirty = super.isDirty();
|
||||
|
||||
if (!dirty) {
|
||||
UiElementNode uiNode = getManifestUiNode(getEditor());
|
||||
if (uiNode != null) {
|
||||
for (UiAttributeNode ui_attr : uiNode.getUiAttributes()) {
|
||||
if (ui_attr.isDirty()) {
|
||||
markDirty();
|
||||
dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dirty) {
|
||||
UiElementNode uiNode = getUsesSdkUiNode(getEditor());
|
||||
if (uiNode != null) {
|
||||
for (UiAttributeNode ui_attr : uiNode.getUiAttributes()) {
|
||||
if (ui_attr.isDirty()) {
|
||||
markDirty();
|
||||
dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to save both the manifest or uses-sdk nodes.
|
||||
* <p/>
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void commit(boolean onSave) {
|
||||
final UiElementNode manifestUiNode = getManifestUiNode(getEditor());
|
||||
final UiElementNode usesSdkUiNode = getUsesSdkUiNode(getEditor());
|
||||
|
||||
getEditor().editXmlModel(new Runnable() {
|
||||
public void run() {
|
||||
if (manifestUiNode != null && manifestUiNode.isDirty()) {
|
||||
for (UiAttributeNode ui_attr : manifestUiNode.getUiAttributes()) {
|
||||
ui_attr.commit();
|
||||
}
|
||||
}
|
||||
|
||||
if (usesSdkUiNode != null && usesSdkUiNode.isDirty()) {
|
||||
for (UiAttributeNode ui_attr : usesSdkUiNode.getUiAttributes()) {
|
||||
ui_attr.commit();
|
||||
}
|
||||
|
||||
if (!usesSdkUiNode.isDirty()) {
|
||||
// Remove the <uses-sdk> XML element if it is empty.
|
||||
// Rather than rely on the internal UI state, actually check that the
|
||||
// XML element has no attributes and no child element so that we don't
|
||||
// trash some user-generated content.
|
||||
Node element = usesSdkUiNode.prepareCommit();
|
||||
if (element != null &&
|
||||
!element.hasAttributes() &&
|
||||
!element.hasChildNodes()) {
|
||||
// Important note: we MUST NOT use usesSdkUiNode.deleteXmlNode()
|
||||
// here, as it would clear the UiAttribute list and thus break the
|
||||
// link between the controls and the ui attribute field.
|
||||
// Instead what we want is simply to remove the XML node and let the
|
||||
// UiElementNode node know.
|
||||
Node parent = element.getParentNode();
|
||||
if (parent != null) {
|
||||
parent.removeChild(element);
|
||||
usesSdkUiNode.loadFromXmlNode(null /*xml_node*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// We need to call super's commit after we synchronized the nodes to make sure we
|
||||
// reset the dirty flag after all the side effects from committing have occurred.
|
||||
super.commit(onSave);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.pages;
|
||||
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.ui.SectionHelper.ManifestSectionPart;
|
||||
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
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 overview page.
|
||||
*/
|
||||
final class OverviewLinksPart extends ManifestSectionPart {
|
||||
|
||||
public OverviewLinksPart(Composite body, FormToolkit toolkit, ManifestEditor editor) {
|
||||
super(body, toolkit, Section.TWISTIE | Section.EXPANDED, true /* description */);
|
||||
Section section = getSection();
|
||||
section.setText("Links");
|
||||
section.setDescription("The content of the Android Manifest is made up of three sections. You can also edit the XML directly.");
|
||||
|
||||
Composite table = createTableLayout(toolkit, 2 /* numColumns */);
|
||||
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append(String.format("<form><li style=\"image\" value=\"app_img\"><a href=\"page:%1$s\">", // $NON-NLS-1$
|
||||
ApplicationPage.PAGE_ID));
|
||||
buf.append("Application");
|
||||
buf.append("</a>"); //$NON-NLS-1$
|
||||
buf.append(": Activities, intent filters, providers, services and receivers.");
|
||||
buf.append("</li>"); //$NON-NLS-1$
|
||||
|
||||
buf.append(String.format("<li style=\"image\" value=\"perm_img\"><a href=\"page:%1$s\">", // $NON-NLS-1$
|
||||
PermissionPage.PAGE_ID));
|
||||
buf.append("Permission");
|
||||
buf.append("</a>"); //$NON-NLS-1$
|
||||
buf.append(": Permissions defined and permissions used.");
|
||||
buf.append("</li>"); //$NON-NLS-1$
|
||||
|
||||
buf.append(String.format("<li style=\"image\" value=\"inst_img\"><a href=\"page:%1$s\">", // $NON-NLS-1$
|
||||
InstrumentationPage.PAGE_ID));
|
||||
buf.append("Instrumentation");
|
||||
buf.append("</a>"); //$NON-NLS-1$
|
||||
buf.append(": Instrumentation defined.");
|
||||
buf.append("</li>"); //$NON-NLS-1$
|
||||
|
||||
buf.append(String.format("<li style=\"image\" value=\"android_img\"><a href=\"page:%1$s\">", // $NON-NLS-1$
|
||||
ManifestEditor.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$
|
||||
|
||||
FormText text = createFormText(table, toolkit, true, buf.toString(),
|
||||
false /* setupLayoutData */);
|
||||
text.setImage("android_img", EditorsPlugin.getAndroidLogo());
|
||||
text.setImage("app_img", getIcon(AndroidManifestDescriptors.APPLICATION_ELEMENT));
|
||||
text.setImage("perm_img", getIcon(AndroidManifestDescriptors.PERMISSION_ELEMENT));
|
||||
text.setImage("inst_img", getIcon(AndroidManifestDescriptors.INTRUMENTATION_ELEMENT));
|
||||
text.addHyperlinkListener(editor.createHyperlinkListener());
|
||||
}
|
||||
|
||||
private Image getIcon(ElementDescriptor desc) {
|
||||
if (desc != null && desc.getIcon() != null) {
|
||||
return desc.getIcon();
|
||||
}
|
||||
|
||||
return EditorsPlugin.getAndroidLogo();
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.pages;
|
||||
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
|
||||
|
||||
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.FormToolkit;
|
||||
import org.eclipse.ui.forms.widgets.ScrolledForm;
|
||||
|
||||
|
||||
/**
|
||||
* Page for overview settings, part of the AndroidManifest form editor.
|
||||
* <p/>
|
||||
* Useful reference:
|
||||
* <a href="http://www.eclipse.org/articles/Article-Forms/article.html">
|
||||
* http://www.eclipse.org/articles/Article-Forms/article.html</a>
|
||||
*/
|
||||
public final class OverviewPage extends FormPage {
|
||||
|
||||
/** Page id used for switching tabs programmatically */
|
||||
final static String PAGE_ID = "overview_page"; //$NON-NLS-1$
|
||||
|
||||
/** Container editor */
|
||||
ManifestEditor mEditor;
|
||||
/** Overview part (attributes for manifest) */
|
||||
private OverviewInfoPart mOverviewPart;
|
||||
|
||||
public OverviewPage(ManifestEditor editor) {
|
||||
super(editor, PAGE_ID, "Overview"); // 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 Manifest Overview");
|
||||
form.setImage(EditorsPlugin.getAndroidLogo());
|
||||
|
||||
Composite body = form.getBody();
|
||||
FormToolkit toolkit = managedForm.getToolkit();
|
||||
ColumnLayout cl = new ColumnLayout();
|
||||
cl.minNumColumns = cl.maxNumColumns = 1;
|
||||
body.setLayout(cl);
|
||||
mOverviewPart = new OverviewInfoPart(body, toolkit, mEditor);
|
||||
managedForm.addPart(mOverviewPart);
|
||||
managedForm.addPart(new OverviewExportPart(this, body, toolkit, mEditor));
|
||||
managedForm.addPart(new OverviewLinksPart(body, toolkit, mEditor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes and refreshes the Application UI node handle by the sub parts.
|
||||
*/
|
||||
public void refreshUiApplicationNode() {
|
||||
if (mOverviewPart != null) {
|
||||
mOverviewPart.onSdkChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.editors.manifest.pages;
|
||||
|
||||
import com.android.ide.eclipse.editors.EditorsPlugin;
|
||||
import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
|
||||
import com.android.ide.eclipse.editors.manifest.ManifestEditor;
|
||||
import com.android.ide.eclipse.editors.manifest.descriptors.AndroidManifestDescriptors;
|
||||
import com.android.ide.eclipse.editors.ui.tree.UiTreeBlock;
|
||||
import com.android.ide.eclipse.editors.uimodel.UiElementNode;
|
||||
|
||||
import org.eclipse.ui.forms.IManagedForm;
|
||||
import org.eclipse.ui.forms.editor.FormPage;
|
||||
import org.eclipse.ui.forms.widgets.ScrolledForm;
|
||||
|
||||
/**
|
||||
* Page for permissions settings, part of the AndroidManifest form editor.
|
||||
* <p/>
|
||||
* Useful reference:
|
||||
* <a href="http://www.eclipse.org/articles/Article-Forms/article.html">
|
||||
* http://www.eclipse.org/articles/Article-Forms/article.html</a>
|
||||
*/
|
||||
public final class PermissionPage extends FormPage {
|
||||
/** Page id used for switching tabs programmatically */
|
||||
public final static String PAGE_ID = "permission_page"; //$NON-NLS-1$
|
||||
|
||||
/** Container editor */
|
||||
ManifestEditor mEditor;
|
||||
|
||||
public PermissionPage(ManifestEditor editor) {
|
||||
super(editor, PAGE_ID, "Permissions"); // tab label, 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 Manifest Permissions");
|
||||
form.setImage(EditorsPlugin.getAndroidLogo());
|
||||
|
||||
UiElementNode manifest = mEditor.getUiRootNode();
|
||||
UiTreeBlock block = new UiTreeBlock(mEditor, manifest,
|
||||
true /* autoCreateRoot */,
|
||||
new ElementDescriptor[] {
|
||||
AndroidManifestDescriptors.PERMISSION_ELEMENT,
|
||||
AndroidManifestDescriptors.USES_PERMISSION_ELEMENT,
|
||||
AndroidManifestDescriptors.PERMISSION_GROUP_ELEMENT,
|
||||
AndroidManifestDescriptors.PERMISSION_TREE_ELEMENT
|
||||
},
|
||||
"Permissions",
|
||||
"List of permissions defined and used by the manifest");
|
||||
block.createContent(managedForm);
|
||||
}
|
||||
}
|
||||