public AndroidLayoutPreviewToolWindowManager( final Project project, final FileEditorManager fileEditorManager) { myProject = project; myFileEditorManager = fileEditorManager; myToolWindowUpdateQueue = new MergingUpdateQueue("android.layout.preview", 100, true, null, project); final MessageBusConnection connection = project.getMessageBus().connect(project); connection.subscribe( FileEditorManagerListener.FILE_EDITOR_MANAGER, new MyFileEditorManagerListener()); connection.subscribe(ProjectTopics.PROJECT_ROOTS, new MyAndroidPlatformListener(project)); PsiManager.getInstance(project) .addPsiTreeChangeListener( new PsiTreeChangeAdapter() { boolean myIgnoreChildrenChanged; @Override public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) { myIgnoreChildrenChanged = false; } @Override public void childrenChanged(@NotNull PsiTreeChangeEvent event) { // See ResourceFolderManager#PsiListener#childrenChanged if (isRelevant(event) && !myIgnoreChildrenChanged && event.getParent() != event.getChild()) { update(event); } } @Override public void childAdded(@NotNull PsiTreeChangeEvent event) { myIgnoreChildrenChanged = true; if (isRelevant(event)) { PsiElement child = event.getChild(); PsiElement parent = event.getParent(); if (child instanceof XmlAttribute && parent instanceof XmlTag) { // Typing in a new attribute. Don't need to do any rendering until there // is an actual value if (((XmlAttribute) child).getValueElement() == null) { return; } } else if (parent instanceof XmlAttribute && child instanceof XmlAttributeValue) { XmlAttributeValue attributeValue = (XmlAttributeValue) child; if (attributeValue.getValue() == null || attributeValue.getValue().isEmpty()) { // Just added a new blank attribute; nothing to render yet return; } } else if (parent instanceof XmlAttributeValue && child instanceof XmlToken && event.getOldChild() == null) { // Just added attribute value String text = child.getText(); // See if this is an attribute that takes a resource! if (text.startsWith(PREFIX_RESOURCE_REF)) { if (text.equals(PREFIX_RESOURCE_REF) || text.equals(ANDROID_PREFIX)) { // Using code completion to insert resource reference; not yet done return; } ResourceUrl url = ResourceUrl.parse(text); if (url != null && url.name.isEmpty()) { // Using code completion to insert resource reference; not yet done return; } } } update(event); } } @Override public void childReplaced(@NotNull PsiTreeChangeEvent event) { myIgnoreChildrenChanged = true; if (isRelevant(event)) { PsiElement child = event.getChild(); PsiElement parent = event.getParent(); if (parent instanceof XmlAttribute && child instanceof XmlToken) { // Typing in attribute name. Don't need to do any rendering until there // is an actual value XmlAttributeValue valueElement = ((XmlAttribute) parent).getValueElement(); if (valueElement == null || valueElement.getValue() == null || valueElement.getValue().isEmpty()) { return; } } else if (parent instanceof XmlAttributeValue && child instanceof XmlToken && event.getOldChild() != null) { String newText = child.getText(); String prevText = event.getOldChild().getText(); // See if user is working on an incomplete URL, and is still not complete, e.g. // typing in @string/foo manually if (newText.startsWith(PREFIX_RESOURCE_REF)) { ResourceUrl prevUrl = ResourceUrl.parse(prevText); ResourceUrl newUrl = ResourceUrl.parse(newText); if (prevUrl != null && prevUrl.name.isEmpty()) { prevUrl = null; } if (newUrl != null && newUrl.name.isEmpty()) { newUrl = null; } if (prevUrl == null && newUrl == null) { return; } } } update(event); } } @Override public void childRemoved(@NotNull PsiTreeChangeEvent event) { myIgnoreChildrenChanged = true; if (isRelevant(event)) { PsiElement child = event.getChild(); PsiElement parent = event.getParent(); if (parent instanceof XmlAttribute && child instanceof XmlToken) { // Typing in attribute name. Don't need to do any rendering until there // is an actual value XmlAttributeValue valueElement = ((XmlAttribute) parent).getValueElement(); if (valueElement == null || valueElement.getValue() == null || valueElement.getValue().isEmpty()) { return; } } update(event); } } }, project); ProjectBuilder.getInstance(project) .addAfterProjectBuildTask( new ProjectBuilder.AfterProjectBuildListener() { @Override protected void buildFinished() { if (myToolWindowForm != null && myToolWindowReady && !myToolWindowDisposed) { ApplicationManager.getApplication() .invokeLater( new Runnable() { @Override public void run() { render(); } }); } } }); }
private void installPropertiesChangeListeners() { final VirtualFileManager virtualFileManager = VirtualFileManager.getInstance(); if (myVfsListener != null) { assert false; virtualFileManager.removeVirtualFileListener(myVfsListener); } myVfsListener = new VirtualFileAdapter() { @Override public void fileCreated(@NotNull VirtualFileEvent event) { if (PropertiesImplUtil.isPropertiesFile(event.getFile(), myProject)) { recreateEditorsPanel(); } } @Override public void fileDeleted(@NotNull VirtualFileEvent event) { for (PropertiesFile file : myEditors.keySet()) { if (Comparing.equal(file.getVirtualFile(), event.getFile())) { recreateEditorsPanel(); return; } } } @Override public void propertyChanged(@NotNull VirtualFilePropertyEvent event) { if (PropertiesImplUtil.isPropertiesFile(event.getFile(), myProject)) { if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) { recreateEditorsPanel(); } else { updateEditorsFromProperties(); } } } }; virtualFileManager.addVirtualFileListener(myVfsListener, this); PsiTreeChangeAdapter psiTreeChangeAdapter = new PsiTreeChangeAdapter() { @Override public void childAdded(@NotNull PsiTreeChangeEvent event) { childrenChanged(event); } @Override public void childRemoved(@NotNull PsiTreeChangeEvent event) { childrenChanged(event); } @Override public void childReplaced(@NotNull PsiTreeChangeEvent event) { childrenChanged(event); } @Override public void childMoved(@NotNull PsiTreeChangeEvent event) { childrenChanged(event); } @Override public void childrenChanged(@NotNull PsiTreeChangeEvent event) { final PsiFile file = event.getFile(); PropertiesFile propertiesFile = PropertiesImplUtil.getPropertiesFile(file); if (propertiesFile == null) return; if (!propertiesFile.getResourceBundle().equals(myResourceBundle)) return; updateEditorsFromProperties(); } }; PsiManager.getInstance(myProject).addPsiTreeChangeListener(psiTreeChangeAdapter, this); }