diff --git a/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java b/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java index ec0f7d6d625f..c81060602c55 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java +++ b/plugins/android-designer/src/com/intellij/android/designer/designSurface/AndroidDesignerEditorPanel.java @@ -1,571 +1,602 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * 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.intellij.android.designer.designSurface; - -import com.android.ide.common.rendering.api.RenderSession; -import com.android.ide.common.resources.configuration.*; -import com.android.sdklib.IAndroidTarget; -import com.intellij.android.designer.actions.ProfileAction; -import com.intellij.android.designer.componentTree.AndroidTreeDecorator; -import com.intellij.android.designer.inspection.ErrorAnalyzer; -import com.intellij.android.designer.model.*; -import com.intellij.android.designer.profile.ProfileManager; -import com.intellij.designer.DesignerEditor; -import com.intellij.designer.DesignerToolWindowManager; -import com.intellij.designer.componentTree.TreeComponentDecorator; -import com.intellij.designer.designSurface.*; -import com.intellij.designer.designSurface.selection.NonResizeSelectionDecorator; -import com.intellij.designer.designSurface.tools.ComponentCreationFactory; -import com.intellij.designer.designSurface.tools.ComponentPasteFactory; -import com.intellij.designer.model.RadComponent; -import com.intellij.designer.model.WrapInProvider; -import com.intellij.designer.palette.DefaultPaletteItem; -import com.intellij.designer.palette.PaletteGroup; -import com.intellij.designer.palette.PaletteItem; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.IdeActions; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ex.ApplicationManagerEx; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.progress.EmptyProgressIndicator; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; -import com.intellij.psi.xml.XmlFile; -import com.intellij.util.Alarm; -import com.intellij.util.PsiNavigateUtil; -import com.intellij.util.ThrowableRunnable; -import org.jetbrains.android.facet.AndroidFacet; -import org.jetbrains.android.maven.AndroidMavenUtil; -import org.jetbrains.android.sdk.AndroidPlatform; -import org.jetbrains.android.sdk.AndroidSdkUtils; -import org.jetbrains.android.uipreview.*; -import org.jetbrains.android.util.AndroidBundle; -import org.jetbrains.android.util.AndroidSdkNotConfiguredException; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.awt.*; -import java.util.List; - -/** - * @author Alexander Lobas - */ -public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { - private final TreeComponentDecorator myTreeDecorator = new AndroidTreeDecorator(); - private final XmlFile myXmlFile; - private final ExternalPSIChangeListener myPSIChangeListener; - private final ProfileAction myProfileAction; - private final Alarm mySessionAlarm = new Alarm(); - private volatile RenderSession mySession; - private boolean myParseTime; - private int myProfileLastVersion; - private WrapInProvider myWrapInProvider; - - public AndroidDesignerEditorPanel(@NotNull DesignerEditor editor, - @NotNull Project project, - @NotNull Module module, - @NotNull VirtualFile file) { - super(editor, project, module, file); - - myXmlFile = (XmlFile)ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public PsiFile compute() { - return PsiManager.getInstance(getProject()).findFile(myFile); - } - }); - myPSIChangeListener = new ExternalPSIChangeListener(this, myXmlFile, 100, new Runnable() { - @Override - public void run() { - reparseFile(); - } - }); - - showProgress("Load configuration"); - myProfileAction = new ProfileAction(this, new Runnable() { - @Override - public void run() { - if (getProject().isDisposed()) { - return; - } - myPSIChangeListener.setInitialize(); - myActionPanel.update(); - if (myRootComponent == null) { - myPSIChangeListener.activate(); - myPSIChangeListener.addRequest(); - } - else if (myProfileLastVersion != myProfileAction.getVersion() || - !ProfileManager.isAndroidSdk(myProfileAction.getCurrentSdk())) { - myPSIChangeListener.addRequest(new Runnable() { - @Override - public void run() { - updateRenderer(true); - } - }); - } - } - }); - - myActionPanel.getPopupGroup().addSeparator(); - AnAction gotoDeclaration = new AnAction("Go To Declaration") { - @Override - public void update(AnActionEvent e) { - EditableArea area = e.getData(EditableArea.DATA_KEY); - e.getPresentation().setEnabled(area != null && area.getSelection().size() == 1); - } - - @Override - public void actionPerformed(AnActionEvent e) { - EditableArea area = e.getData(EditableArea.DATA_KEY); - RadViewComponent component = (RadViewComponent)area.getSelection().get(0); - PsiNavigateUtil.navigate(component.getTag()); - } - }; - myActionPanel.registerAction(gotoDeclaration, IdeActions.ACTION_GOTO_DECLARATION); - myActionPanel.getPopupGroup().add(gotoDeclaration); - } - - private void reparseFile() { - try { - storeState(); - showDesignerCard(); - - parseFile(new Runnable() { - @Override - public void run() { - showDesignerCard(); - myLayeredPane.revalidate(); - restoreState(); - } - }); - } - catch (RuntimeException e) { - myPSIChangeListener.clear(); - showError("Parsing error", e.getCause() == null ? e : e.getCause()); - } - } - - private void parseFile(final Runnable runnable) { - myParseTime = true; - - final ModelParser parser = new ModelParser(getProject(), myXmlFile); - - createRenderer(parser.getLayoutXmlText(), new ThrowableRunnable() { - @Override - public void run() throws Throwable { - RootView rootView = new RootView(mySession.getImage(), 30, 20); - parser.updateRootComponent(mySession, rootView); - RadViewComponent newRootComponent = parser.getRootComponent(); - - newRootComponent.setClientProperty(ModelParser.XML_FILE_KEY, myXmlFile); - newRootComponent.setClientProperty(ModelParser.MODULE_KEY, AndroidDesignerEditorPanel.this); - newRootComponent.setClientProperty(TreeComponentDecorator.KEY, myTreeDecorator); - - PropertyParser propertyParser = new PropertyParser(getModule(), myProfileAction.getProfileManager().getSelectedTarget()); - newRootComponent.setClientProperty(PropertyParser.KEY, propertyParser); - propertyParser.loadRecursive(newRootComponent); - - JPanel rootPanel = new JPanel(null); - rootPanel.setBackground(Color.WHITE); - rootPanel.add(rootView); - - removeNativeRoot(); - myRootComponent = newRootComponent; - loadInspections(new EmptyProgressIndicator()); - updateInspections(); - myLayeredPane.add(rootPanel, LAYER_COMPONENT); - - myParseTime = false; - - runnable.run(); - } - }); - } - - private void createRenderer(final String layoutXmlText, final ThrowableRunnable runnable) { - disposeRenderer(); - - ApplicationManager.getApplication().saveAll(); - - mySessionAlarm.addRequest(new Runnable() { - @Override - public void run() { - if (mySession == null) { - showProgress("Create RenderLib"); - } - } - }, 500); - - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - @Override - public void run() { - try { - long time = System.currentTimeMillis(); - - myProfileLastVersion = myProfileAction.getVersion(); - - AndroidPlatform platform = AndroidPlatform.getInstance(getModule()); - if (platform == null) { - throw new AndroidSdkNotConfiguredException(); - } - - AndroidFacet facet = AndroidFacet.getInstance(getModule()); - ProfileManager manager = myProfileAction.getProfileManager(); - - LayoutDeviceConfiguration deviceConfiguration = manager.getSelectedDeviceConfiguration(); - if (deviceConfiguration == null) { - throw new RenderingException("Device is not specified"); - } - - FolderConfiguration config = new FolderConfiguration(); - config.set(deviceConfiguration.getConfiguration()); - config.setUiModeQualifier(new UiModeQualifier(manager.getSelectedDockMode())); - config.setNightModeQualifier(new NightModeQualifier(manager.getSelectedNightMode())); - - LocaleData locale = manager.getSelectedLocale(); - if (locale == null) { - throw new RenderingException("Locale is not specified"); - } - config.setLanguageQualifier(new LanguageQualifier(locale.getLanguage())); - config.setRegionQualifier(new RegionQualifier(locale.getRegion())); - - float xdpi = deviceConfiguration.getDevice().getXDpi(); - float ydpi = deviceConfiguration.getDevice().getYDpi(); - - IAndroidTarget target = manager.getSelectedTarget(); - ThemeData theme = manager.getSelectedTheme(); - - if (target == null || theme == null) { - throw new RenderingException(); - } - - RenderingResult result = - RenderUtil.renderLayout(getModule(), layoutXmlText, myFile, null, target, facet, config, xdpi, ydpi, theme, 10000, true); - - if (ApplicationManagerEx.getApplicationEx().isInternal()) { - System.out.println("Render time: " + (System.currentTimeMillis() - time)); // XXX - } - - if (result == null) { - throw new RenderingException(); - } - - mySession = result.getSession(); - mySessionAlarm.cancelAllRequests(); - - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - try { - if (!getProject().isDisposed()) { - hideProgress(); - runnable.run(); - } - } - catch (Throwable e) { - myPSIChangeListener.clear(); - showError("Parsing error", e); - myParseTime = false; - } - } - }); - } - catch (final Throwable e) { - myPSIChangeListener.clear(); - mySessionAlarm.cancelAllRequests(); - - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - myPSIChangeListener.clear(); - showError("Render error", e); - myParseTime = false; - } - }); - } - } - }); - } - - private void disposeRenderer() { - if (mySession != null) { - mySession.dispose(); - mySession = null; - } - } - - private void updateRenderer(final boolean updateProperties) { - myParseTime = true; - final String layoutXmlText = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String compute() { - if (ModelParser.checkTag(myXmlFile.getRootTag())) { - return myXmlFile.getText(); - } - return ModelParser.NO_ROOT_CONTENT; - } - }); - createRenderer(layoutXmlText, new ThrowableRunnable() { - @Override - public void run() throws Throwable { - RadViewComponent rootComponent = (RadViewComponent)myRootComponent; - RootView rootView = (RootView)rootComponent.getNativeComponent(); - rootView.setImage(mySession.getImage()); - ModelParser.updateRootComponent(rootComponent, mySession, rootView); - - myParseTime = false; - - myLayeredPane.revalidate(); - myHorizontalCaption.update(); - myVerticalCaption.update(); - - DesignerToolWindowManager.getInstance(getProject()).refresh(updateProperties); - } - }); - } - - private void removeNativeRoot() { - if (myRootComponent != null) { - myLayeredPane.remove(((RadViewComponent)myRootComponent).getNativeComponent().getParent()); - } - } - - @Override - protected void configureError(@NotNull ErrorInfo info) { - if (info.myThrowable instanceof AndroidSdkNotConfiguredException) { - info.myShowLog = false; - info.myShowStack = false; - - if (AndroidMavenUtil.isMavenizedModule(getModule())) { - info.myMessages.add(new FixableMessageInfo(true, AndroidBundle.message("android.maven.cannot.parse.android.sdk.error", - getModule().getName()), "", "", null, null)); - } - else { - info.myMessages.add(new FixableMessageInfo(true, "Please ", "configure", " Android SDK", new Runnable() { - @Override - public void run() { - AndroidSdkUtils.openModuleDependenciesConfigurable(getModule()); - } - }, null)); - } - } - else { - List warnMessages = null; - boolean renderError = info.myThrowable instanceof RenderingException; - - if (renderError) { - RenderingException exception = (RenderingException)info.myThrowable; - warnMessages = exception.getWarnMessages(); - - if (StringUtil.isEmpty(exception.getPresentableMessage())) { - Throwable[] causes = exception.getCauses(); - if (causes.length == 0) { - info.myThrowable = new Exception(AndroidBundle.message("android.layout.preview.default.error.message"), exception); - } - else if (causes.length == 1) { - info.myThrowable = causes[0]; - } - else { - info.myThrowable = new RenderingException(AndroidBundle.message("android.layout.preview.default.error.message"), causes); - } - } - } - - if (warnMessages == null) { - info.myShowMessage = myParseTime || renderError; - info.myShowLog = !renderError; - } - else { - info.myShowLog = false; - info.myShowStack = true; - - for (FixableIssueMessage message : warnMessages) { - info.myMessages.add( - new FixableMessageInfo(false, message.myBeforeLinkText, message.myLinkText, message.myAfterLinkText, message.myQuickFix, - message.myAdditionalFixes)); - } - } - } - - StringBuilder builder = new StringBuilder(); - - builder.append("ActiveTool: ").append(myToolProvider.getActiveTool()); - builder.append("\nSDK: "); - - try { - AndroidPlatform platform = AndroidPlatform.getInstance(getModule()); - IAndroidTarget target = platform.getTarget(); - builder.append(target.getFullName()).append(" - ").append(target.getVersion()); - } - catch (Throwable e) { - builder.append(""); - } - - info.myMessage = builder.toString(); - } - - @Override - protected void showErrorPage(ErrorInfo info) { - myPSIChangeListener.clear(); - mySessionAlarm.cancelAllRequests(); - removeNativeRoot(); - super.showErrorPage(info); - } - - public ProfileAction getProfileAction() { - return myProfileAction; - } - - @Override - public void activate() { - myProfileAction.externalUpdate(); - myPSIChangeListener.activate(); - - if (myPSIChangeListener.isUpdateRenderer()) { - updateRenderer(true); - } - } - - @Override - public void deactivate() { - myPSIChangeListener.deactivate(); - } - - @Override - public void dispose() { - myPSIChangeListener.dispose(); - super.dispose(); - disposeRenderer(); - } - - @Override - public String getPlatformTarget() { - return "android"; - } - - @Override - public TreeComponentDecorator getTreeDecorator() { - return myTreeDecorator; - } - - @Override - public WrapInProvider getWrapInProvider() { - if (myWrapInProvider == null) { - myWrapInProvider = new AndroidWrapInProvider(getProject()); - } - return myWrapInProvider; - } - - private static final ComponentDecorator NON_RESIZE_DECORATOR = new NonResizeSelectionDecorator(Color.RED, 2); - - @Override - protected ComponentDecorator getRootSelectionDecorator() { - return NON_RESIZE_DECORATOR; - } - - @Override - protected EditOperation processRootOperation(OperationContext context) { - return null; - } - - @Override - public List getPaletteGroups() { - return ViewsMetaManager.getInstance(getProject()).getPaletteGroups(); - } - - @Override - @NotNull - protected ComponentCreationFactory createCreationFactory(final PaletteItem paletteItem) { - return new ComponentCreationFactory() { - @NotNull - @Override - public RadComponent create() throws Exception { - RadViewComponent component = ModelParser.createComponent(null, ((DefaultPaletteItem)paletteItem).getMetaModel()); - if (component instanceof IConfigurableComponent) { - ((IConfigurableComponent)component).configure(myRootComponent); - } - return component; - } - }; - } - - @Override - public ComponentPasteFactory createPasteFactory(String xmlComponents) { - return new AndroidPasteFactory(getProject(), xmlComponents); - } - - @Override - public String getEditorText() { - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public String compute() { - return myXmlFile.getText(); - } - }); - } - - @Override - protected boolean execute(ThrowableRunnable operation, boolean updateProperties) { - try { - myPSIChangeListener.stop(); - operation.run(); - updateRenderer(updateProperties); - return true; - } - catch (Throwable e) { - showError("Execute command", e); - return false; - } - finally { - myPSIChangeListener.start(); - } - } - - @Override - protected void execute(List operations) { - try { - myPSIChangeListener.stop(); - for (EditOperation operation : operations) { - operation.execute(); - } - updateRenderer(true); - } - catch (Throwable e) { - showError("Execute command", e); - } - finally { - myPSIChangeListener.start(); - } - } - - ////////////////////////////////////////////////////////////////////////////////////////// - // - // Inspections - // - ////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public void loadInspections(ProgressIndicator progress) { - if (myRootComponent != null) { - ErrorAnalyzer.load(getProject(), myXmlFile, myRootComponent, progress); - } - } +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * 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.intellij.android.designer.designSurface; + +import com.android.ide.common.rendering.api.RenderSession; +import com.android.ide.common.resources.configuration.*; +import com.android.sdklib.IAndroidTarget; +import com.intellij.android.designer.actions.ProfileAction; +import com.intellij.android.designer.componentTree.AndroidTreeDecorator; +import com.intellij.android.designer.inspection.ErrorAnalyzer; +import com.intellij.android.designer.model.*; +import com.intellij.android.designer.profile.ProfileManager; +import com.intellij.designer.DesignerEditor; +import com.intellij.designer.DesignerToolWindowManager; +import com.intellij.designer.componentTree.TreeComponentDecorator; +import com.intellij.designer.designSurface.*; +import com.intellij.designer.designSurface.selection.NonResizeSelectionDecorator; +import com.intellij.designer.designSurface.tools.ComponentCreationFactory; +import com.intellij.designer.designSurface.tools.ComponentPasteFactory; +import com.intellij.designer.model.RadComponent; +import com.intellij.designer.model.WrapInProvider; +import com.intellij.designer.palette.DefaultPaletteItem; +import com.intellij.designer.palette.PaletteGroup; +import com.intellij.designer.palette.PaletteItem; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ex.ApplicationManagerEx; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.progress.EmptyProgressIndicator; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.Alarm; +import com.intellij.util.PsiNavigateUtil; +import com.intellij.util.ThrowableRunnable; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.maven.AndroidMavenUtil; +import org.jetbrains.android.sdk.AndroidPlatform; +import org.jetbrains.android.sdk.AndroidSdkUtils; +import org.jetbrains.android.uipreview.*; +import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidSdkNotConfiguredException; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.List; + +/** + * @author Alexander Lobas + */ +public final class AndroidDesignerEditorPanel extends DesignerEditorPanel { + private final TreeComponentDecorator myTreeDecorator = new AndroidTreeDecorator(); + private final XmlFile myXmlFile; + private final ExternalPSIChangeListener myPSIChangeListener; + private final ProfileAction myProfileAction; + private final Alarm mySessionAlarm = new Alarm(); + private volatile RenderSession mySession; + private boolean myParseTime; + private int myProfileLastVersion; + private WrapInProvider myWrapInProvider; + + public AndroidDesignerEditorPanel(@NotNull DesignerEditor editor, + @NotNull Project project, + @NotNull Module module, + @NotNull VirtualFile file) { + super(editor, project, module, file); + + myXmlFile = (XmlFile)ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiFile compute() { + return PsiManager.getInstance(getProject()).findFile(myFile); + } + }); + myPSIChangeListener = new ExternalPSIChangeListener(this, myXmlFile, 100, new Runnable() { + @Override + public void run() { + reparseFile(); + } + }); + + showProgress("Load configuration"); + myProfileAction = new ProfileAction(this, new Runnable() { + @Override + public void run() { + if (getProject().isDisposed()) { + return; + } + myPSIChangeListener.setInitialize(); + myActionPanel.update(); + if (myRootComponent == null) { + myPSIChangeListener.activate(); + myPSIChangeListener.addRequest(); + } + else if (myProfileLastVersion != myProfileAction.getVersion() || + !ProfileManager.isAndroidSdk(myProfileAction.getCurrentSdk())) { + myPSIChangeListener.addRequest(new Runnable() { + @Override + public void run() { + updateRenderer(true); + } + }); + } + } + }); + + myActionPanel.getPopupGroup().addSeparator(); + AnAction gotoDeclaration = new AnAction("Go To Declaration") { + @Override + public void update(AnActionEvent e) { + EditableArea area = e.getData(EditableArea.DATA_KEY); + e.getPresentation().setEnabled(area != null && area.getSelection().size() == 1); + } + + @Override + public void actionPerformed(AnActionEvent e) { + EditableArea area = e.getData(EditableArea.DATA_KEY); + RadViewComponent component = (RadViewComponent)area.getSelection().get(0); + PsiNavigateUtil.navigate(component.getTag()); + } + }; + myActionPanel.registerAction(gotoDeclaration, IdeActions.ACTION_GOTO_DECLARATION); + myActionPanel.getPopupGroup().add(gotoDeclaration); + } + + private void reparseFile() { + try { + storeState(); + showDesignerCard(); + + parseFile(new Runnable() { + @Override + public void run() { + showDesignerCard(); + myLayeredPane.revalidate(); + restoreState(); + } + }); + } + catch (RuntimeException e) { + myPSIChangeListener.clear(); + showError("Parsing error", e.getCause() == null ? e : e.getCause()); + } + } + + private void parseFile(final Runnable runnable) { + myParseTime = true; + + final ModelParser parser = new ModelParser(getProject(), myXmlFile); + + createRenderer(parser.getLayoutXmlText(), new MyThrowable(), new ThrowableRunnable() { + @Override + public void run() throws Throwable { + RootView rootView = new RootView(mySession.getImage(), 30, 20); + parser.updateRootComponent(mySession, rootView); + RadViewComponent newRootComponent = parser.getRootComponent(); + + newRootComponent.setClientProperty(ModelParser.XML_FILE_KEY, myXmlFile); + newRootComponent.setClientProperty(ModelParser.MODULE_KEY, AndroidDesignerEditorPanel.this); + newRootComponent.setClientProperty(TreeComponentDecorator.KEY, myTreeDecorator); + + PropertyParser propertyParser = + new PropertyParser(getModule(), myProfileAction.getProfileManager().getSelectedTarget()); + newRootComponent.setClientProperty(PropertyParser.KEY, propertyParser); + propertyParser.loadRecursive(newRootComponent); + + JPanel rootPanel = new JPanel(null); + rootPanel.setBackground(Color.WHITE); + rootPanel.add(rootView); + + removeNativeRoot(); + myRootComponent = newRootComponent; + loadInspections(new EmptyProgressIndicator()); + updateInspections(); + myLayeredPane.add(rootPanel, LAYER_COMPONENT); + + myParseTime = false; + + runnable.run(); + } + }); + } + + private void createRenderer(final String layoutXmlText, final MyThrowable throwable, final ThrowableRunnable runnable) { + disposeRenderer(); + + ApplicationManager.getApplication().saveAll(); + + mySessionAlarm.addRequest(new Runnable() { + @Override + public void run() { + if (mySession == null) { + showProgress("Create RenderLib"); + } + } + }, 500); + + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + try { + long time = System.currentTimeMillis(); + + myProfileLastVersion = myProfileAction.getVersion(); + + AndroidPlatform platform = AndroidPlatform.getInstance(getModule()); + if (platform == null) { + throw new AndroidSdkNotConfiguredException(); + } + + AndroidFacet facet = AndroidFacet.getInstance(getModule()); + ProfileManager manager = myProfileAction.getProfileManager(); + + LayoutDeviceConfiguration deviceConfiguration = manager.getSelectedDeviceConfiguration(); + if (deviceConfiguration == null) { + throw new RenderingException("Device is not specified"); + } + + FolderConfiguration config = new FolderConfiguration(); + config.set(deviceConfiguration.getConfiguration()); + config.setUiModeQualifier(new UiModeQualifier(manager.getSelectedDockMode())); + config.setNightModeQualifier(new NightModeQualifier(manager.getSelectedNightMode())); + + LocaleData locale = manager.getSelectedLocale(); + if (locale == null) { + throw new RenderingException("Locale is not specified"); + } + config.setLanguageQualifier(new LanguageQualifier(locale.getLanguage())); + config.setRegionQualifier(new RegionQualifier(locale.getRegion())); + + float xdpi = deviceConfiguration.getDevice().getXDpi(); + float ydpi = deviceConfiguration.getDevice().getYDpi(); + + IAndroidTarget target = manager.getSelectedTarget(); + ThemeData theme = manager.getSelectedTheme(); + + if (target == null || theme == null) { + throw new RenderingException(); + } + + RenderingResult result = + RenderUtil.renderLayout(getModule(), layoutXmlText, myFile, null, target, facet, config, xdpi, ydpi, theme, 10000, true); + + if (ApplicationManagerEx.getApplicationEx().isInternal()) { + System.out.println("Render time: " + (System.currentTimeMillis() - time)); // XXX + } + + if (result == null) { + throw new RenderingException(); + } + + mySession = result.getSession(); + mySessionAlarm.cancelAllRequests(); + + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + try { + if (!getProject().isDisposed()) { + hideProgress(); + runnable.run(); + } + } + catch (Throwable e) { + myPSIChangeListener.clear(); + showError("Parsing error", throwable.wrap(e)); + myParseTime = false; + } + } + }); + } + catch (final Throwable e) { + myPSIChangeListener.clear(); + mySessionAlarm.cancelAllRequests(); + + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + myPSIChangeListener.clear(); + showError("Render error", throwable.wrap(e)); + myParseTime = false; + } + }); + } + } + }); + } + + private void disposeRenderer() { + if (mySession != null) { + mySession.dispose(); + mySession = null; + } + } + + private void updateRenderer(final boolean updateProperties) { + myParseTime = true; + final String layoutXmlText = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public String compute() { + if (ModelParser.checkTag(myXmlFile.getRootTag())) { + return myXmlFile.getText(); + } + return ModelParser.NO_ROOT_CONTENT; + } + }); + createRenderer(layoutXmlText, new MyThrowable(), new ThrowableRunnable() { + @Override + public void run() throws Throwable { + RadViewComponent rootComponent = (RadViewComponent)myRootComponent; + RootView rootView = (RootView)rootComponent.getNativeComponent(); + rootView.setImage(mySession.getImage()); + ModelParser.updateRootComponent(rootComponent, mySession, rootView); + + myParseTime = false; + + myLayeredPane.revalidate(); + myHorizontalCaption.update(); + myVerticalCaption.update(); + + DesignerToolWindowManager.getInstance(getProject()).refresh(updateProperties); + } + }); + } + + private void removeNativeRoot() { + if (myRootComponent != null) { + myLayeredPane.remove(((RadViewComponent)myRootComponent).getNativeComponent().getParent()); + } + } + + @Override + protected void configureError(@NotNull ErrorInfo info) { + Throwable renderCreator = null; + if (info.myThrowable instanceof MyThrowable) { + renderCreator = info.myThrowable; + info.myThrowable = ((MyThrowable)info.myThrowable).original; + } + + if (info.myThrowable instanceof AndroidSdkNotConfiguredException) { + info.myShowLog = false; + info.myShowStack = false; + + if (AndroidMavenUtil.isMavenizedModule(getModule())) { + info.myMessages.add(new FixableMessageInfo(true, AndroidBundle.message("android.maven.cannot.parse.android.sdk.error", + getModule().getName()), "", "", null, null)); + } + else { + info.myMessages.add(new FixableMessageInfo(true, "Please ", "configure", " Android SDK", new Runnable() { + @Override + public void run() { + AndroidSdkUtils.openModuleDependenciesConfigurable(getModule()); + } + }, null)); + } + } + else { + List warnMessages = null; + boolean renderError = info.myThrowable instanceof RenderingException; + + if (renderError) { + RenderingException exception = (RenderingException)info.myThrowable; + warnMessages = exception.getWarnMessages(); + + if (StringUtil.isEmpty(exception.getPresentableMessage())) { + Throwable[] causes = exception.getCauses(); + if (causes.length == 0) { + info.myThrowable = new Exception(AndroidBundle.message("android.layout.preview.default.error.message"), exception); + } + else if (causes.length == 1) { + info.myThrowable = causes[0]; + } + else { + info.myThrowable = new RenderingException(AndroidBundle.message("android.layout.preview.default.error.message"), causes); + } + } + } + + if (warnMessages == null) { + info.myShowMessage = myParseTime || renderError; + info.myShowLog = !renderError; + } + else { + info.myShowLog = false; + info.myShowStack = true; + + for (FixableIssueMessage message : warnMessages) { + info.myMessages.add( + new FixableMessageInfo(false, message.myBeforeLinkText, message.myLinkText, message.myAfterLinkText, message.myQuickFix, + message.myAdditionalFixes)); + } + } + } + + StringBuilder builder = new StringBuilder(); + + builder.append("ActiveTool: ").append(myToolProvider.getActiveTool()); + builder.append("\nSDK: "); + + try { + AndroidPlatform platform = AndroidPlatform.getInstance(getModule()); + IAndroidTarget target = platform.getTarget(); + builder.append(target.getFullName()).append(" - ").append(target.getVersion()); + } + catch (Throwable e) { + builder.append(""); + } + + if (renderCreator != null) { + builder.append("\nCreateRendererStack:\n"); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + renderCreator.printStackTrace(new PrintStream(stream)); + builder.append(stream.toString()); + } + + info.myMessage = builder.toString(); + } + + @Override + protected void showErrorPage(ErrorInfo info) { + myPSIChangeListener.clear(); + mySessionAlarm.cancelAllRequests(); + removeNativeRoot(); + super.showErrorPage(info); + } + + public ProfileAction getProfileAction() { + return myProfileAction; + } + + @Override + public void activate() { + myProfileAction.externalUpdate(); + myPSIChangeListener.activate(); + + if (myPSIChangeListener.isUpdateRenderer()) { + updateRenderer(true); + } + } + + @Override + public void deactivate() { + myPSIChangeListener.deactivate(); + } + + @Override + public void dispose() { + myPSIChangeListener.dispose(); + super.dispose(); + disposeRenderer(); + } + + @Override + public String getPlatformTarget() { + return "android"; + } + + @Override + public TreeComponentDecorator getTreeDecorator() { + return myTreeDecorator; + } + + @Override + public WrapInProvider getWrapInProvider() { + if (myWrapInProvider == null) { + myWrapInProvider = new AndroidWrapInProvider(getProject()); + } + return myWrapInProvider; + } + + private static final ComponentDecorator NON_RESIZE_DECORATOR = new NonResizeSelectionDecorator(Color.RED, 2); + + @Override + protected ComponentDecorator getRootSelectionDecorator() { + return NON_RESIZE_DECORATOR; + } + + @Override + protected EditOperation processRootOperation(OperationContext context) { + return null; + } + + @Override + public List getPaletteGroups() { + return ViewsMetaManager.getInstance(getProject()).getPaletteGroups(); + } + + @Override + @NotNull + protected ComponentCreationFactory createCreationFactory(final PaletteItem paletteItem) { + return new ComponentCreationFactory() { + @NotNull + @Override + public RadComponent create() throws Exception { + RadViewComponent component = ModelParser.createComponent(null, ((DefaultPaletteItem)paletteItem).getMetaModel()); + if (component instanceof IConfigurableComponent) { + ((IConfigurableComponent)component).configure(myRootComponent); + } + return component; + } + }; + } + + @Override + public ComponentPasteFactory createPasteFactory(String xmlComponents) { + return new AndroidPasteFactory(getProject(), xmlComponents); + } + + @Override + public String getEditorText() { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public String compute() { + return myXmlFile.getText(); + } + }); + } + + @Override + protected boolean execute(ThrowableRunnable operation, boolean updateProperties) { + try { + myPSIChangeListener.stop(); + operation.run(); + updateRenderer(updateProperties); + return true; + } + catch (Throwable e) { + showError("Execute command", e); + return false; + } + finally { + myPSIChangeListener.start(); + } + } + + @Override + protected void execute(List operations) { + try { + myPSIChangeListener.stop(); + for (EditOperation operation : operations) { + operation.execute(); + } + updateRenderer(true); + } + catch (Throwable e) { + showError("Execute command", e); + } + finally { + myPSIChangeListener.start(); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // Inspections + // + ////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void loadInspections(ProgressIndicator progress) { + if (myRootComponent != null) { + ErrorAnalyzer.load(getProject(), myXmlFile, myRootComponent, progress); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // + // + ////////////////////////////////////////////////////////////////////////////////////////// + + private static class MyThrowable extends Throwable { + public Throwable original; + + public MyThrowable wrap(Throwable original) { + this.original = original; + return this; + } + } } \ No newline at end of file