private boolean isRelevant(PsiTreeChangeEvent event) {
    if (myToolWindowForm == null || !myToolWindowReady || myToolWindowDisposed) {
      return false;
    }
    final PsiFile fileInPreview = myToolWindowForm.getFile();
    final PsiFile file = event.getFile();

    if (fileInPreview == null || file == null || fileInPreview != file) {
      return false;
    }

    PsiElement child = event.getChild();
    PsiElement parent = event.getParent();

    // We can ignore edits in whitespace, and in XML error nodes, and in comments
    // (Note that editing text in an attribute value, including whitespace characters,
    // is not a PsiWhiteSpace element; it's an XmlToken of token type XML_ATTRIBUTE_VALUE_TOKEN
    if (child instanceof PsiWhiteSpace
        || child instanceof PsiErrorElement
        || child instanceof XmlComment
        || parent instanceof XmlComment) {
      return false;
    }

    return true;
  }
  public boolean render() {
    ApplicationManager.getApplication().assertIsDispatchThread();

    if (myToolWindow == null || !myToolWindow.isVisible()) {
      return false;
    }

    final PsiFile psiFile = myToolWindowForm.getFile();
    if (psiFile == null) {
      return false;
    }

    final AndroidFacet facet = AndroidFacet.getInstance(psiFile);
    if (facet == null) {
      return false;
    }

    getRenderingQueue()
        .queue(
            new Update("render") {
              @Override
              public void run() {
                ProgressManager.getInstance()
                    .runProcess(
                        new Runnable() {
                          @Override
                          public void run() {
                            DumbService.getInstance(myProject).waitForSmartMode();
                            try {
                              doRender(facet, psiFile);
                            } catch (Throwable e) {
                              LOG.error(e);
                            }
                            synchronized (PROGRESS_LOCK) {
                              if (myCurrentIndicator != null) {
                                myCurrentIndicator.stop();
                                myCurrentIndicator = null;
                              }
                            }
                          }
                        },
                        new AndroidPreviewProgressIndicator(myToolWindowForm, 100));
              }

              @Override
              public boolean canEat(Update update) {
                return true;
              }
            });
    return true;
  }
  private void initToolWindow() {
    myToolWindowForm = new AndroidLayoutPreviewToolWindowForm(this);
    final String toolWindowId = AndroidBundle.message("android.layout.preview.tool.window.title");
    myToolWindow =
        ToolWindowManager.getInstance(myProject)
            .registerToolWindow(toolWindowId, false, ToolWindowAnchor.RIGHT, myProject, true);
    myToolWindow.setIcon(AndroidIcons.AndroidPreview);

    ((ToolWindowManagerEx) ToolWindowManager.getInstance(myProject))
        .addToolWindowManagerListener(
            new ToolWindowManagerAdapter() {
              private boolean myVisible = false;

              @Override
              public void stateChanged() {
                if (myProject.isDisposed()) {
                  return;
                }

                final ToolWindow window =
                    ToolWindowManager.getInstance(myProject).getToolWindow(toolWindowId);
                if (window != null && window.isAvailable()) {
                  final boolean visible = window.isVisible();
                  AndroidEditorSettings.getInstance().getGlobalState().setVisible(visible);

                  if (visible && !myVisible) {
                    render();
                  }
                  myVisible = visible;
                }
              }
            });

    final JPanel contentPanel = myToolWindowForm.getContentPanel();
    final ContentManager contentManager = myToolWindow.getContentManager();
    @SuppressWarnings("ConstantConditions")
    final Content content = contentManager.getFactory().createContent(contentPanel, null, false);
    content.setDisposer(myToolWindowForm);
    content.setCloseable(false);
    content.setPreferredFocusableComponent(contentPanel);
    contentManager.addContent(content);
    contentManager.setSelectedContent(content, true);
    myToolWindow.setAvailable(false, null);
  }
  private void doRender(@NotNull final AndroidFacet facet, @NotNull final PsiFile psiFile) {
    if (myProject.isDisposed()) {
      return;
    }

    final AndroidLayoutPreviewToolWindowForm toolWindowForm = myToolWindowForm;
    if (toolWindowForm == null) {
      return;
    }

    final VirtualFile layoutXmlFile = psiFile.getVirtualFile();
    if (layoutXmlFile == null) {
      return;
    }
    Module module = facet.getModule();
    Configuration configuration = toolWindowForm.getConfiguration();
    if (configuration == null) {
      return;
    }

    // Some types of files must be saved to disk first, because layoutlib doesn't
    // delegate XML parsers for non-layout files (meaning layoutlib will read the
    // disk contents, so we have to push any edits to disk before rendering)
    LayoutPullParserFactory.saveFileIfNecessary(psiFile);

    RenderResult result = null;
    synchronized (RENDERING_LOCK) {
      final RenderLogger logger = new RenderLogger(layoutXmlFile.getName(), module);
      final RenderService service =
          RenderService.create(facet, module, psiFile, configuration, logger, toolWindowForm);
      if (service != null) {
        service.useDesignMode(psiFile);
        result = service.render();
        service.dispose();
      }
      if (result == null) {
        result = RenderResult.createBlank(psiFile, logger);
      }
    }

    if (!getRenderingQueue().isEmpty()) {
      return;
    }

    final RenderResult renderResult = result;
    ApplicationManager.getApplication()
        .invokeLater(
            new Runnable() {
              @Override
              public void run() {
                if (!myToolWindowReady || myToolWindowDisposed) {
                  return;
                }
                final TextEditor editor =
                    getActiveLayoutXmlEditor(); // Must be run from read thread
                myToolWindowForm.setRenderResult(renderResult, editor);
                myToolWindowForm.updatePreviewPanel();

                if (RenderPreviewMode.getCurrent() != RenderPreviewMode.NONE) {
                  RenderPreviewManager previewManager =
                      myToolWindowForm.getPreviewPanel().getPreviewManager(myToolWindowForm, true);
                  if (previewManager != null) {
                    previewManager.renderPreviews();
                  }
                }
              }
            });
  }