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]

Reply via email to