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