amondel2 commented on code in PR #441: URL: https://github.com/apache/grails-intellij-plugin/pull/441#discussion_r4290180382
########## plugin/src/main/java/org/apache/grails/intellij/plugin/config/GrailsCustomFoldersConfigurable.java: ########## @@ -0,0 +1,240 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * https://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 org.apache.grails.intellij.plugin.config; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.project.Project; +import com.intellij.ui.DocumentAdapter; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.ui.components.JBTextArea; +import com.intellij.util.ui.FormBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.apache.grails.intellij.plugin.projectView.GrailsProjectViewPanes; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.event.DocumentEvent; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * The single custom-folders settings page. It edits the global defaults (application service) and + * this project's own folders (project service) together and previews the folders the Grails view + * will actually show. The global defaults apply to every Grails project; this project's folders + * are added to them. + */ +public class GrailsCustomFoldersConfigurable implements Configurable { + + private final @NotNull GrailsCustomFoldersAppService appService; + private final @NotNull GrailsCustomFoldersProjectService projectService; + private final @NotNull Project project; + + private @Nullable JPanel component; + private @Nullable JBTextArea globalArea; + private @Nullable JBTextArea overrideArea; + private @Nullable JBTextArea effectiveArea; + private @Nullable JBLabel sourceLabel; + private @Nullable JBLabel warningLabel; + + public GrailsCustomFoldersConfigurable(@NotNull Project project) { + this.project = project; + this.appService = ApplicationManager.getApplication().getService(GrailsCustomFoldersAppService.class); + this.projectService = project.getService(GrailsCustomFoldersProjectService.class); + } + + @Override + public @NotNull String getDisplayName() { + return "Custom Folders Configuration"; + } + + @Override + public @NotNull JComponent createComponent() { + if (component == null) { + globalArea = foldersArea(true); + overrideArea = foldersArea(true); + effectiveArea = foldersArea(false); + + DocumentAdapter refreshPreview = new DocumentAdapter() { + @Override + protected void textChanged(@NotNull DocumentEvent e) { + updateEffectivePreview(); + } + }; + globalArea.getDocument().addDocumentListener(refreshPreview); + overrideArea.getDocument().addDocumentListener(refreshPreview); + + sourceLabel = new JBLabel(); + warningLabel = new JBLabel(); + warningLabel.setIcon(AllIcons.General.Warning); + + JBLabel description = new JBLabel( + "<html>Relative paths are resolved against the project root; absolute paths are used as-is.<br>" + + "The Grails view renders at most " + GrailsCustomFoldersProjectService.MAX_FOLDERS + + " folders in total: this project's folders come first, then the global defaults.</html>"); + + component = FormBuilder.createFormBuilder() + .addComponent(description) + .addComponent(warningLabel) + .addLabeledComponent(new JBLabel("Global defaults (up to " + + GrailsCustomFoldersProjectService.MAX_FOLDERS + " folders):"), + new JBScrollPane(globalArea), 1, false) + .addLabeledComponent(new JBLabel("This project's folders (up to " + + GrailsCustomFoldersProjectService.MAX_FOLDERS + " folders):"), + new JBScrollPane(overrideArea), 1, false) + .addComponent(sourceLabel) + .addLabeledComponent(new JBLabel("Effective folders in the Grails view:"), + new JBScrollPane(effectiveArea), 1, false) + .getPanel(); + + reset(); + } + return component; + } + + @Override + public boolean isModified() { + if (globalArea != null && !parseFolders(globalArea.getText()).equals(appService.getFolders())) return true; + return overrideArea != null && !parseFolders(overrideArea.getText()).equals(projectService.getFolders()); + } + + @Override + public void apply() throws ConfigurationException { + // The global defaults have no change event, so repaint here: editing them changes what every + // Grails project renders, and the view would otherwise keep the old folders until something + // else triggered a refresh. + if (globalArea != null) appService.setFolders(parseFolders(globalArea.getText())); + if (overrideArea != null) projectService.setFolders(parseFolders(overrideArea.getText())); + GrailsProjectViewPanes.refresh(project); Review Comment: Fixed by publishing rather than fanning out. `GrailsCustomFoldersAppService` publishes `GlobalFoldersChangedListener` on the **application** bus, and every open Grails project holds its own subscription, so a global change reaches every open pane. I deliberately did not fan out centrally over `ProjectManager.getOpenProjects()`: with N projects open that issues N refreshes per subscriber, so one Apply would cost N² tree rebuilds to reach panes that each already have a subscription of their own. The subscriber coalesces through one flag held per project, so the settings dialog writing both the global defaults and this project's folders costs one rebuild per open project rather than two. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
