Home | History | Annotate | Download | only in gre
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.editors.layout.gre;
     18 
     19 import static com.android.SdkConstants.ANDROID_URI;
     20 import static com.android.SdkConstants.ATTR_ID;
     21 import static com.android.SdkConstants.AUTO_URI;
     22 import static com.android.SdkConstants.CLASS_FRAGMENT;
     23 import static com.android.SdkConstants.CLASS_V4_FRAGMENT;
     24 import static com.android.SdkConstants.CLASS_VIEW;
     25 import static com.android.SdkConstants.NEW_ID_PREFIX;
     26 import static com.android.SdkConstants.URI_PREFIX;
     27 
     28 import com.android.annotations.NonNull;
     29 import com.android.annotations.Nullable;
     30 import com.android.ide.common.api.IClientRulesEngine;
     31 import com.android.ide.common.api.INode;
     32 import com.android.ide.common.api.IValidator;
     33 import com.android.ide.common.api.IViewMetadata;
     34 import com.android.ide.common.api.IViewRule;
     35 import com.android.ide.common.api.Margins;
     36 import com.android.ide.common.api.Rect;
     37 import com.android.ide.common.layout.BaseViewRule;
     38 import com.android.ide.common.resources.ResourceRepository;
     39 import com.android.ide.eclipse.adt.AdtPlugin;
     40 import com.android.ide.eclipse.adt.internal.actions.AddSupportJarAction;
     41 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
     42 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
     43 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
     44 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser;
     45 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
     46 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
     47 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.LayoutCanvas;
     48 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderService;
     49 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionManager;
     50 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ViewHierarchy;
     51 import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
     52 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
     53 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
     54 import com.android.ide.eclipse.adt.internal.refactorings.core.RenameResult;
     55 import com.android.ide.eclipse.adt.internal.resources.CyclicDependencyValidator;
     56 import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
     57 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
     58 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
     59 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
     60 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
     61 import com.android.ide.eclipse.adt.internal.ui.MarginChooser;
     62 import com.android.ide.eclipse.adt.internal.ui.ReferenceChooserDialog;
     63 import com.android.ide.eclipse.adt.internal.ui.ResourceChooser;
     64 import com.android.ide.eclipse.adt.internal.ui.ResourcePreviewHelper;
     65 import com.android.resources.ResourceType;
     66 import com.android.sdklib.IAndroidTarget;
     67 
     68 import org.eclipse.core.resources.IProject;
     69 import org.eclipse.core.runtime.CoreException;
     70 import org.eclipse.core.runtime.NullProgressMonitor;
     71 import org.eclipse.jdt.core.Flags;
     72 import org.eclipse.jdt.core.IJavaProject;
     73 import org.eclipse.jdt.core.IPackageFragment;
     74 import org.eclipse.jdt.core.IPackageFragmentRoot;
     75 import org.eclipse.jdt.core.IType;
     76 import org.eclipse.jdt.core.ITypeHierarchy;
     77 import org.eclipse.jdt.core.JavaModelException;
     78 import org.eclipse.jdt.core.search.IJavaSearchScope;
     79 import org.eclipse.jdt.core.search.SearchEngine;
     80 import org.eclipse.jdt.ui.IJavaElementSearchConstants;
     81 import org.eclipse.jdt.ui.JavaUI;
     82 import org.eclipse.jdt.ui.actions.OpenNewClassWizardAction;
     83 import org.eclipse.jdt.ui.dialogs.ITypeInfoFilterExtension;
     84 import org.eclipse.jdt.ui.dialogs.ITypeInfoRequestor;
     85 import org.eclipse.jdt.ui.dialogs.TypeSelectionExtension;
     86 import org.eclipse.jdt.ui.wizards.NewClassWizardPage;
     87 import org.eclipse.jface.dialogs.IDialogConstants;
     88 import org.eclipse.jface.dialogs.IInputValidator;
     89 import org.eclipse.jface.dialogs.InputDialog;
     90 import org.eclipse.jface.dialogs.MessageDialog;
     91 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
     92 import org.eclipse.jface.window.Window;
     93 import org.eclipse.swt.SWT;
     94 import org.eclipse.swt.events.SelectionAdapter;
     95 import org.eclipse.swt.events.SelectionEvent;
     96 import org.eclipse.swt.layout.GridLayout;
     97 import org.eclipse.swt.widgets.Button;
     98 import org.eclipse.swt.widgets.Composite;
     99 import org.eclipse.swt.widgets.Control;
    100 import org.eclipse.swt.widgets.Display;
    101 import org.eclipse.swt.widgets.Shell;
    102 import org.eclipse.ui.dialogs.SelectionDialog;
    103 import org.w3c.dom.Document;
    104 import org.w3c.dom.Element;
    105 import org.w3c.dom.Node;
    106 import org.w3c.dom.NodeList;
    107 
    108 import java.util.Collection;
    109 import java.util.Collections;
    110 import java.util.HashSet;
    111 import java.util.List;
    112 import java.util.Map;
    113 import java.util.Set;
    114 import java.util.concurrent.atomic.AtomicReference;
    115 
    116 /**
    117  * Implementation of {@link IClientRulesEngine}. This provides {@link IViewRule} clients
    118  * with a few methods they can use to access functionality from this {@link RulesEngine}.
    119  */
    120 class ClientRulesEngine implements IClientRulesEngine {
    121     /** The return code from the dialog for the user choosing "Clear" */
    122     public static final int CLEAR_RETURN_CODE = -5;
    123     /** The dialog button ID for the user choosing "Clear" */
    124     private static final int CLEAR_BUTTON_ID = CLEAR_RETURN_CODE;
    125 
    126     private final RulesEngine mRulesEngine;
    127     private final String mFqcn;
    128 
    129     public ClientRulesEngine(RulesEngine rulesEngine, String fqcn) {
    130         mRulesEngine = rulesEngine;
    131         mFqcn = fqcn;
    132     }
    133 
    134     @Override
    135     public @NonNull String getFqcn() {
    136         return mFqcn;
    137     }
    138 
    139     @Override
    140     public void debugPrintf(@NonNull String msg, Object... params) {
    141         AdtPlugin.printToConsole(
    142                 mFqcn == null ? "<unknown>" : mFqcn,
    143                 String.format(msg, params)
    144                 );
    145     }
    146 
    147     @Override
    148     public IViewRule loadRule(@NonNull String fqcn) {
    149         return mRulesEngine.loadRule(fqcn, fqcn);
    150     }
    151 
    152     @Override
    153     public void displayAlert(@NonNull String message) {
    154         MessageDialog.openInformation(
    155                 AdtPlugin.getShell(),
    156                 mFqcn,  // title
    157                 message);
    158     }
    159 
    160     @Override
    161     public boolean rename(INode node) {
    162         GraphicalEditorPart editor = mRulesEngine.getEditor();
    163         SelectionManager manager = editor.getCanvasControl().getSelectionManager();
    164         RenameResult result = manager.performRename(node, null);
    165 
    166         return !result.isCanceled() && !result.isUnavailable();
    167     }
    168 
    169     @Override
    170     public String displayInput(@NonNull String message, @Nullable String value,
    171             final @Nullable IValidator filter) {
    172         IInputValidator validator = null;
    173         if (filter != null) {
    174             validator = new IInputValidator() {
    175                 @Override
    176                 public String isValid(String newText) {
    177                     // IValidator has the same interface as SWT's IInputValidator
    178                     try {
    179                         return filter.validate(newText);
    180                     } catch (Exception e) {
    181                         AdtPlugin.log(e, "Custom validator failed: %s", e.toString());
    182                         return ""; //$NON-NLS-1$
    183                     }
    184                 }
    185             };
    186         }
    187 
    188         InputDialog d = new InputDialog(
    189                     AdtPlugin.getShell(),
    190                     mFqcn,  // title
    191                     message,
    192                     value == null ? "" : value, //$NON-NLS-1$
    193                     validator) {
    194             @Override
    195             protected void createButtonsForButtonBar(Composite parent) {
    196                 createButton(parent, CLEAR_BUTTON_ID, "Clear", false /*defaultButton*/);
    197                 super.createButtonsForButtonBar(parent);
    198             }
    199 
    200             @Override
    201             protected void buttonPressed(int buttonId) {
    202                 super.buttonPressed(buttonId);
    203 
    204                 if (buttonId == CLEAR_BUTTON_ID) {
    205                     assert CLEAR_RETURN_CODE != Window.OK && CLEAR_RETURN_CODE != Window.CANCEL;
    206                     setReturnCode(CLEAR_RETURN_CODE);
    207                     close();
    208                 }
    209             }
    210         };
    211         int result = d.open();
    212         if (result == ResourceChooser.CLEAR_RETURN_CODE) {
    213             return "";
    214         } else if (result == Window.OK) {
    215             return d.getValue();
    216         }
    217         return null;
    218     }
    219 
    220     @Override
    221     @Nullable
    222     public Object getViewObject(@NonNull INode node) {
    223         ViewHierarchy views = mRulesEngine.getEditor().getCanvasControl().getViewHierarchy();
    224         CanvasViewInfo vi = views.findViewInfoFor(node);
    225         if (vi != null) {
    226             return vi.getViewObject();
    227         }
    228 
    229         return null;
    230     }
    231 
    232     @Override
    233     public @NonNull IViewMetadata getMetadata(final @NonNull String fqcn) {
    234         return new IViewMetadata() {
    235             @Override
    236             public @NonNull String getDisplayName() {
    237                 // This also works when there is no "."
    238                 return fqcn.substring(fqcn.lastIndexOf('.') + 1);
    239             }
    240 
    241             @Override
    242             public @NonNull FillPreference getFillPreference() {
    243                 return ViewMetadataRepository.get().getFillPreference(fqcn);
    244             }
    245 
    246             @Override
    247             public @NonNull Margins getInsets() {
    248                 return mRulesEngine.getEditor().getCanvasControl().getInsets(fqcn);
    249             }
    250 
    251             @Override
    252             public @NonNull List<String> getTopAttributes() {
    253                 return ViewMetadataRepository.get().getTopAttributes(fqcn);
    254             }
    255         };
    256     }
    257 
    258     @Override
    259     public int getMinApiLevel() {
    260         Sdk currentSdk = Sdk.getCurrent();
    261         if (currentSdk != null) {
    262             IAndroidTarget target = currentSdk.getTarget(mRulesEngine.getEditor().getProject());
    263             if (target != null) {
    264                 return target.getVersion().getApiLevel();
    265             }
    266         }
    267 
    268         return -1;
    269     }
    270 
    271     @Override
    272     public IValidator getResourceValidator(
    273             @NonNull final String resourceTypeName, final boolean uniqueInProject,
    274             final boolean uniqueInLayout, final boolean exists, final String... allowed) {
    275         return new IValidator() {
    276             private ResourceNameValidator mValidator;
    277 
    278             @Override
    279             public String validate(@NonNull String text) {
    280                 if (mValidator == null) {
    281                     ResourceType type = ResourceType.getEnum(resourceTypeName);
    282                     if (uniqueInLayout) {
    283                         assert !uniqueInProject;
    284                         assert !exists;
    285                         Set<String> existing = new HashSet<String>();
    286                         Document doc = mRulesEngine.getEditor().getModel().getXmlDocument();
    287                         if (doc != null) {
    288                             addIds(doc, existing);
    289                         }
    290                         for (String s : allowed) {
    291                             existing.remove(s);
    292                         }
    293                         mValidator = ResourceNameValidator.create(false, existing, type);
    294                     } else {
    295                         assert allowed.length == 0;
    296                         IProject project = mRulesEngine.getEditor().getProject();
    297                         mValidator = ResourceNameValidator.create(false, project, type);
    298                         if (uniqueInProject) {
    299                             mValidator.unique();
    300                         }
    301                     }
    302                     if (exists) {
    303                         mValidator.exist();
    304                     }
    305                 }
    306 
    307                 return mValidator.isValid(text);
    308             }
    309         };
    310     }
    311 
    312     /** Find declared ids under the given DOM node */
    313     private static void addIds(Node node, Set<String> ids) {
    314         if (node.getNodeType() == Node.ELEMENT_NODE) {
    315             Element element = (Element) node;
    316             String id = element.getAttributeNS(ANDROID_URI, ATTR_ID);
    317             if (id != null && id.startsWith(NEW_ID_PREFIX)) {
    318                 ids.add(BaseViewRule.stripIdPrefix(id));
    319             }
    320         }
    321 
    322         NodeList children = node.getChildNodes();
    323         for (int i = 0, n = children.getLength(); i < n; i++) {
    324             Node child = children.item(i);
    325             addIds(child, ids);
    326         }
    327     }
    328 
    329     @Override
    330     public String displayReferenceInput(@Nullable String currentValue) {
    331         GraphicalEditorPart graphicalEditor = mRulesEngine.getEditor();
    332         LayoutEditorDelegate delegate = graphicalEditor.getEditorDelegate();
    333         IProject project = delegate.getEditor().getProject();
    334         if (project != null) {
    335             // get the resource repository for this project and the system resources.
    336             ResourceRepository projectRepository =
    337                 ResourceManager.getInstance().getProjectResources(project);
    338             Shell shell = AdtPlugin.getShell();
    339             if (shell == null) {
    340                 return null;
    341             }
    342             ReferenceChooserDialog dlg = new ReferenceChooserDialog(
    343                     project,
    344                     projectRepository,
    345                     shell);
    346             dlg.setPreviewHelper(new ResourcePreviewHelper(dlg, graphicalEditor));
    347 
    348             dlg.setCurrentResource(currentValue);
    349 
    350             if (dlg.open() == Window.OK) {
    351                 return dlg.getCurrentResource();
    352             }
    353         }
    354 
    355         return null;
    356     }
    357 
    358     @Override
    359     public String displayResourceInput(@NonNull String resourceTypeName,
    360             @Nullable String currentValue) {
    361         return displayResourceInput(resourceTypeName, currentValue, null);
    362     }
    363 
    364     private String displayResourceInput(String resourceTypeName, String currentValue,
    365             IInputValidator validator) {
    366         ResourceType type = ResourceType.getEnum(resourceTypeName);
    367         GraphicalEditorPart graphicalEditor = mRulesEngine.getEditor();
    368         return ResourceChooser.chooseResource(graphicalEditor, type, currentValue, validator);
    369     }
    370 
    371     @Override
    372     public String[] displayMarginInput(@Nullable String all, @Nullable String left,
    373             @Nullable String right, @Nullable String top, @Nullable String bottom) {
    374         GraphicalEditorPart editor = mRulesEngine.getEditor();
    375         IProject project = editor.getProject();
    376         if (project != null) {
    377             Shell shell = AdtPlugin.getShell();
    378             if (shell == null) {
    379                 return null;
    380             }
    381             AndroidTargetData data = editor.getEditorDelegate().getEditor().getTargetData();
    382             MarginChooser dialog = new MarginChooser(shell, editor, data, all, left, right,
    383                     top, bottom);
    384             if (dialog.open() == Window.OK) {
    385                 return dialog.getMargins();
    386             }
    387         }
    388 
    389         return null;
    390     }
    391 
    392     @Override
    393     public String displayIncludeSourceInput() {
    394         AndroidXmlEditor editor = mRulesEngine.getEditor().getEditorDelegate().getEditor();
    395         IInputValidator validator = CyclicDependencyValidator.create(editor.getInputFile());
    396         return displayResourceInput(ResourceType.LAYOUT.getName(), null, validator);
    397     }
    398 
    399     @Override
    400     public void select(final @NonNull Collection<INode> nodes) {
    401         LayoutCanvas layoutCanvas = mRulesEngine.getEditor().getCanvasControl();
    402         final SelectionManager selectionManager = layoutCanvas.getSelectionManager();
    403         selectionManager.select(nodes);
    404         // ALSO run an async select since immediately after nodes are created they
    405         // may not be selectable. We can't ONLY run an async exec since
    406         // code may depend on operating on the selection.
    407         layoutCanvas.getDisplay().asyncExec(new Runnable() {
    408             @Override
    409             public void run() {
    410                 selectionManager.select(nodes);
    411             }
    412         });
    413     }
    414 
    415     @Override
    416     public String displayFragmentSourceInput() {
    417         try {
    418             // Compute a search scope: We need to merge all the subclasses
    419             // android.app.Fragment and android.support.v4.app.Fragment
    420             IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
    421             IProject project = mRulesEngine.getProject();
    422             final IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
    423             if (javaProject != null) {
    424                 IType oldFragmentType = javaProject.findType(CLASS_V4_FRAGMENT);
    425 
    426                 // First check to make sure fragments are available, and if not,
    427                 // warn the user.
    428                 IAndroidTarget target = Sdk.getCurrent().getTarget(project);
    429                 // No, this should be using the min SDK instead!
    430                 if (target.getVersion().getApiLevel() < 11 && oldFragmentType == null) {
    431                     // Compatibility library must be present
    432                     MessageDialog dialog =
    433                         new MessageDialog(
    434                                 Display.getCurrent().getActiveShell(),
    435                           "Fragment Warning",
    436                           null,
    437                           "Fragments require API level 11 or higher, or a compatibility "
    438                           + "library for older versions.\n\n"
    439                           + " Do you want to install the compatibility library?",
    440                           MessageDialog.QUESTION,
    441                           new String[] { "Install", "Cancel" },
    442                           1 /* default button: Cancel */);
    443                       int answer = dialog.open();
    444                       if (answer == 0) {
    445                           if (!AddSupportJarAction.install(project)) {
    446                               return null;
    447                           }
    448                       } else {
    449                           return null;
    450                       }
    451                 }
    452 
    453                 // Look up sub-types of each (new fragment class and compatibility fragment
    454                 // class, if any) and merge the two arrays - then create a scope from these
    455                 // elements.
    456                 IType[] fragmentTypes = new IType[0];
    457                 IType[] oldFragmentTypes = new IType[0];
    458                 if (oldFragmentType != null) {
    459                     ITypeHierarchy hierarchy =
    460                         oldFragmentType.newTypeHierarchy(new NullProgressMonitor());
    461                     oldFragmentTypes = hierarchy.getAllSubtypes(oldFragmentType);
    462                 }
    463                 IType fragmentType = javaProject.findType(CLASS_FRAGMENT);
    464                 if (fragmentType != null) {
    465                     ITypeHierarchy hierarchy =
    466                         fragmentType.newTypeHierarchy(new NullProgressMonitor());
    467                     fragmentTypes = hierarchy.getAllSubtypes(fragmentType);
    468                 }
    469                 IType[] subTypes = new IType[fragmentTypes.length + oldFragmentTypes.length];
    470                 System.arraycopy(fragmentTypes, 0, subTypes, 0, fragmentTypes.length);
    471                 System.arraycopy(oldFragmentTypes, 0, subTypes, fragmentTypes.length,
    472                         oldFragmentTypes.length);
    473                 scope = SearchEngine.createJavaSearchScope(subTypes, IJavaSearchScope.SOURCES);
    474             }
    475 
    476             Shell parent = AdtPlugin.getShell();
    477             final AtomicReference<String> returnValue =
    478                 new AtomicReference<String>();
    479             final AtomicReference<SelectionDialog> dialogHolder =
    480                 new AtomicReference<SelectionDialog>();
    481             final SelectionDialog dialog = JavaUI.createTypeDialog(
    482                     parent,
    483                     new ProgressMonitorDialog(parent),
    484                     scope,
    485                     IJavaElementSearchConstants.CONSIDER_CLASSES, false,
    486                     // Use ? as a default filter to fill dialog with matches
    487                     "?", //$NON-NLS-1$
    488                     new TypeSelectionExtension() {
    489                         @Override
    490                         public Control createContentArea(Composite parentComposite) {
    491                             Composite composite = new Composite(parentComposite, SWT.NONE);
    492                             composite.setLayout(new GridLayout(1, false));
    493                             Button button = new Button(composite, SWT.PUSH);
    494                             button.setText("Create New...");
    495                             button.addSelectionListener(new SelectionAdapter() {
    496                                 @Override
    497                                 public void widgetSelected(SelectionEvent e) {
    498                                     String fqcn = createNewFragmentClass(javaProject);
    499                                     if (fqcn != null) {
    500                                         returnValue.set(fqcn);
    501                                         dialogHolder.get().close();
    502                                     }
    503                                 }
    504                             });
    505                             return composite;
    506                         }
    507 
    508                         @Override
    509                         public ITypeInfoFilterExtension getFilterExtension() {
    510                             return new ITypeInfoFilterExtension() {
    511                                 @Override
    512                                 public boolean select(ITypeInfoRequestor typeInfoRequestor) {
    513                                     int modifiers = typeInfoRequestor.getModifiers();
    514                                     if (!Flags.isPublic(modifiers)
    515                                             || Flags.isInterface(modifiers)
    516                                             || Flags.isEnum(modifiers)
    517                                             || Flags.isAbstract(modifiers)) {
    518                                         return false;
    519                                     }
    520                                     return true;
    521                                 }
    522                             };
    523                         }
    524                     });
    525             dialogHolder.set(dialog);
    526 
    527             dialog.setTitle("Choose Fragment Class");
    528             dialog.setMessage("Select a Fragment class (? = any character, * = any string):");
    529             if (dialog.open() == IDialogConstants.CANCEL_ID) {
    530                 return null;
    531             }
    532             if (returnValue.get() != null) {
    533                 return returnValue.get();
    534             }
    535 
    536             Object[] types = dialog.getResult();
    537             if (types != null && types.length > 0) {
    538                 return ((IType) types[0]).getFullyQualifiedName();
    539             }
    540         } catch (JavaModelException e) {
    541             AdtPlugin.log(e, null);
    542         } catch (CoreException e) {
    543             AdtPlugin.log(e, null);
    544         }
    545         return null;
    546     }
    547 
    548     @Override
    549     public String displayCustomViewClassInput() {
    550         try {
    551             IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
    552             IProject project = mRulesEngine.getProject();
    553             final IJavaProject javaProject = BaseProjectHelper.getJavaProject(project);
    554             if (javaProject != null) {
    555                 // Look up sub-types of each (new fragment class and compatibility fragment
    556                 // class, if any) and merge the two arrays - then create a scope from these
    557                 // elements.
    558                 IType[] viewTypes = new IType[0];
    559                 IType fragmentType = javaProject.findType(CLASS_VIEW);
    560                 if (fragmentType != null) {
    561                     ITypeHierarchy hierarchy =
    562                         fragmentType.newTypeHierarchy(new NullProgressMonitor());
    563                     viewTypes = hierarchy.getAllSubtypes(fragmentType);
    564                 }
    565                 scope = SearchEngine.createJavaSearchScope(viewTypes, IJavaSearchScope.SOURCES);
    566             }
    567 
    568             Shell parent = AdtPlugin.getShell();
    569             final AtomicReference<String> returnValue =
    570                 new AtomicReference<String>();
    571             final AtomicReference<SelectionDialog> dialogHolder =
    572                 new AtomicReference<SelectionDialog>();
    573             final SelectionDialog dialog = JavaUI.createTypeDialog(
    574                     parent,
    575                     new ProgressMonitorDialog(parent),
    576                     scope,
    577                     IJavaElementSearchConstants.CONSIDER_CLASSES, false,
    578                     // Use ? as a default filter to fill dialog with matches
    579                     "?", //$NON-NLS-1$
    580                     new TypeSelectionExtension() {
    581                         @Override
    582                         public Control createContentArea(Composite parentComposite) {
    583                             Composite composite = new Composite(parentComposite, SWT.NONE);
    584                             composite.setLayout(new GridLayout(1, false));
    585                             Button button = new Button(composite, SWT.PUSH);
    586                             button.setText("Create New...");
    587                             button.addSelectionListener(new SelectionAdapter() {
    588                                 @Override
    589                                 public void widgetSelected(SelectionEvent e) {
    590                                     String fqcn = createNewCustomViewClass(javaProject);
    591                                     if (fqcn != null) {
    592                                         returnValue.set(fqcn);
    593                                         dialogHolder.get().close();
    594                                     }
    595                                 }
    596                             });
    597                             return composite;
    598                         }
    599 
    600                         @Override
    601                         public ITypeInfoFilterExtension getFilterExtension() {
    602                             return new ITypeInfoFilterExtension() {
    603                                 @Override
    604                                 public boolean select(ITypeInfoRequestor typeInfoRequestor) {
    605                                     int modifiers = typeInfoRequestor.getModifiers();
    606                                     if (!Flags.isPublic(modifiers)
    607                                             || Flags.isInterface(modifiers)
    608                                             || Flags.isEnum(modifiers)
    609                                             || Flags.isAbstract(modifiers)) {
    610                                         return false;
    611                                     }
    612                                     return true;
    613                                 }
    614                             };
    615                         }
    616                     });
    617             dialogHolder.set(dialog);
    618 
    619             dialog.setTitle("Choose Custom View Class");
    620             dialog.setMessage("Select a Custom View class (? = any character, * = any string):");
    621             if (dialog.open() == IDialogConstants.CANCEL_ID) {
    622                 return null;
    623             }
    624             if (returnValue.get() != null) {
    625                 return returnValue.get();
    626             }
    627 
    628             Object[] types = dialog.getResult();
    629             if (types != null && types.length > 0) {
    630                 return ((IType) types[0]).getFullyQualifiedName();
    631             }
    632         } catch (JavaModelException e) {
    633             AdtPlugin.log(e, null);
    634         } catch (CoreException e) {
    635             AdtPlugin.log(e, null);
    636         }
    637         return null;
    638     }
    639 
    640     @Override
    641     public void redraw() {
    642         mRulesEngine.getEditor().getCanvasControl().redraw();
    643     }
    644 
    645     @Override
    646     public void layout() {
    647         mRulesEngine.getEditor().recomputeLayout();
    648     }
    649 
    650     @Override
    651     public Map<INode, Rect> measureChildren(@NonNull INode parent,
    652             @Nullable IClientRulesEngine.AttributeFilter filter) {
    653         RenderService renderService = RenderService.create(mRulesEngine.getEditor());
    654         Map<INode, Rect> map = renderService.measureChildren(parent, filter);
    655         if (map == null) {
    656             map = Collections.emptyMap();
    657         }
    658         return map;
    659     }
    660 
    661     @Override
    662     public int pxToDp(int px) {
    663         ConfigurationChooser chooser = mRulesEngine.getEditor().getConfigurationChooser();
    664         float dpi = chooser.getConfiguration().getDensity().getDpiValue();
    665         return (int) (px * 160 / dpi);
    666     }
    667 
    668     @Override
    669     public int dpToPx(int dp) {
    670         ConfigurationChooser chooser = mRulesEngine.getEditor().getConfigurationChooser();
    671         float dpi = chooser.getConfiguration().getDensity().getDpiValue();
    672         return (int) (dp * dpi / 160);
    673     }
    674 
    675     @Override
    676     public int screenToLayout(int pixels) {
    677         return (int) (pixels / mRulesEngine.getEditor().getCanvasControl().getScale());
    678     }
    679 
    680     private String createNewFragmentClass(IJavaProject javaProject) {
    681         NewClassWizardPage page = new NewClassWizardPage();
    682 
    683         IProject project = mRulesEngine.getProject();
    684         Sdk sdk = Sdk.getCurrent();
    685         if (sdk == null) {
    686             return null;
    687         }
    688         IAndroidTarget target = sdk.getTarget(project);
    689         String superClass;
    690         if (target == null || target.getVersion().getApiLevel() < 11) {
    691             superClass = CLASS_V4_FRAGMENT;
    692         } else {
    693             superClass = CLASS_FRAGMENT;
    694         }
    695         page.setSuperClass(superClass, true /* canBeModified */);
    696         IPackageFragmentRoot root = ManifestInfo.getSourcePackageRoot(javaProject);
    697         if (root != null) {
    698             page.setPackageFragmentRoot(root, true /* canBeModified */);
    699         }
    700         ManifestInfo manifestInfo = ManifestInfo.get(project);
    701         IPackageFragment pkg = manifestInfo.getPackageFragment();
    702         if (pkg != null) {
    703             page.setPackageFragment(pkg, true /* canBeModified */);
    704         }
    705         OpenNewClassWizardAction action = new OpenNewClassWizardAction();
    706         action.setConfiguredWizardPage(page);
    707         action.run();
    708         IType createdType = page.getCreatedType();
    709         if (createdType != null) {
    710             return createdType.getFullyQualifiedName();
    711         } else {
    712             return null;
    713         }
    714     }
    715 
    716     private String createNewCustomViewClass(IJavaProject javaProject) {
    717         NewClassWizardPage page = new NewClassWizardPage();
    718 
    719         IProject project = mRulesEngine.getProject();
    720         String superClass = CLASS_VIEW;
    721         page.setSuperClass(superClass, true /* canBeModified */);
    722         IPackageFragmentRoot root = ManifestInfo.getSourcePackageRoot(javaProject);
    723         if (root != null) {
    724             page.setPackageFragmentRoot(root, true /* canBeModified */);
    725         }
    726         ManifestInfo manifestInfo = ManifestInfo.get(project);
    727         IPackageFragment pkg = manifestInfo.getPackageFragment();
    728         if (pkg != null) {
    729             page.setPackageFragment(pkg, true /* canBeModified */);
    730         }
    731         OpenNewClassWizardAction action = new OpenNewClassWizardAction();
    732         action.setConfiguredWizardPage(page);
    733         action.run();
    734         IType createdType = page.getCreatedType();
    735         if (createdType != null) {
    736             return createdType.getFullyQualifiedName();
    737         } else {
    738             return null;
    739         }
    740     }
    741 
    742     @Override
    743     public @NonNull String getUniqueId(@NonNull String fqcn) {
    744         UiDocumentNode root = mRulesEngine.getEditor().getModel();
    745         String prefix = fqcn.substring(fqcn.lastIndexOf('.') + 1);
    746         prefix = Character.toLowerCase(prefix.charAt(0)) + prefix.substring(1);
    747         return DescriptorsUtils.getFreeWidgetId(root, prefix);
    748     }
    749 
    750     @Override
    751     public @NonNull String getAppNameSpace() {
    752         IProject project = mRulesEngine.getEditor().getProject();
    753 
    754         ProjectState projectState = Sdk.getProjectState(project);
    755         if (projectState != null && projectState.isLibrary()) {
    756             return AUTO_URI;
    757         }
    758 
    759         ManifestInfo info = ManifestInfo.get(project);
    760         return URI_PREFIX + info.getPackage();
    761     }
    762 }
    763