Cleanup: NotNull/Nullable
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / extractMethod / preview / PreviewPanel.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.refactoring.extractMethod.preview;
3
4 import com.intellij.ide.IdeBundle;
5 import com.intellij.ide.actions.exclusion.ExclusionHandler;
6 import com.intellij.openapi.Disposable;
7 import com.intellij.openapi.actionSystem.CommonDataKeys;
8 import com.intellij.openapi.actionSystem.DataProvider;
9 import com.intellij.openapi.application.ApplicationManager;
10 import com.intellij.openapi.project.DumbService;
11 import com.intellij.openapi.project.Project;
12 import com.intellij.openapi.ui.Messages;
13 import com.intellij.openapi.util.Disposer;
14 import com.intellij.pom.Navigatable;
15 import com.intellij.psi.PsiDocumentManager;
16 import com.intellij.psi.impl.PsiDocumentManagerBase;
17 import com.intellij.refactoring.RefactoringBundle;
18 import com.intellij.refactoring.extractMethod.ExtractMethodProcessor;
19 import com.intellij.ui.*;
20 import com.intellij.ui.content.Content;
21 import com.intellij.usages.impl.UsageModelTracker;
22 import com.intellij.util.Alarm;
23 import com.intellij.util.ui.DialogUtil;
24 import com.intellij.util.ui.JBUI;
25 import com.intellij.util.ui.components.BorderLayoutPanel;
26 import one.util.streamex.StreamEx;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
29
30 import javax.swing.*;
31 import javax.swing.tree.DefaultMutableTreeNode;
32 import java.awt.*;
33 import java.util.List;
34 import java.util.Optional;
35
36 /**
37  * @author Pavel.Dolgov
38  */
39 class PreviewPanel extends BorderLayoutPanel implements Disposable, DataProvider {
40   private final Project myProject;
41   private final PreviewTree myTree;
42   private final ExclusionHandler<DefaultMutableTreeNode> myExclusionHandler;
43   private final ButtonsPanel myButtonsPanel;
44   private Content myContent;
45   private final PreviewDiffPanel myDiffPanel;
46   private final Alarm myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
47
48   PreviewPanel(ExtractMethodProcessor processor) {
49     myProject = processor.getProject();
50     myTree = new PreviewTree(processor);
51     JScrollPane treePane = ScrollPaneFactory.createScrollPane(myTree.getComponent());
52     treePane.setBorder(IdeBorderFactory.createBorder(SideBorder.BOTTOM));
53
54     myDiffPanel = new PreviewDiffPanel(processor, myTree);
55     myTree.addTreeListener(myDiffPanel);
56
57     BorderLayoutPanel leftPanel = new BorderLayoutPanel();
58     leftPanel.addToCenter(treePane);
59     myButtonsPanel = new ButtonsPanel(myProject);
60     leftPanel.addToBottom(myButtonsPanel);
61
62     JBSplitter splitter = new OnePixelSplitter(false);
63     splitter.setProportion(0.3f);
64     splitter.setFirstComponent(leftPanel);
65     splitter.setSecondComponent(myDiffPanel);
66     addToCenter(splitter);
67
68     myExclusionHandler = new PreviewExclusionHandler(this);
69
70     UsageModelTracker usageModelTracker = new UsageModelTracker(myProject);
71     Disposer.register(this, usageModelTracker);
72     usageModelTracker.addListener(isPropertyChange -> updateLater(), this);
73
74     Disposer.register(processor.getProject(), this);
75     Disposer.register(this, myTree);
76     Disposer.register(this, myDiffPanel);
77   }
78
79   @Nullable
80   @Override
81   public Object getData(@NotNull String dataId) {
82     if (ExclusionHandler.EXCLUSION_HANDLER.is(dataId)) {
83       return myExclusionHandler;
84     }
85     if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
86       List<FragmentNode> selectedNodes = myTree.getSelectedNodes();
87       if (selectedNodes.size() == 1) {
88         return Optional.ofNullable(selectedNodes.get(0))
89                        .map(FragmentNode::getNavigatable)
90                        .map(n -> new Navigatable[]{n})
91                        .orElse(null);
92       }
93     }
94     if (CommonDataKeys.NAVIGATABLE_ARRAY.is(dataId)) {
95       List<FragmentNode> selectedNodes = myTree.getSelectedNodes();
96       if (!selectedNodes.isEmpty()) {
97         return StreamEx.of(selectedNodes)
98                        .map(FragmentNode::getNavigatable)
99                        .nonNull()
100                        .toArray(Navigatable[]::new);
101       }
102     }
103     return null;
104   }
105
106   public void setContent(Content content) {
107     myContent = content;
108     content.setDisposer(this);
109   }
110
111   public void initLater() {
112     myDiffPanel.initLater();
113   }
114
115   @Override
116   public void dispose() {
117   }
118
119   private void close() {
120     if (myContent != null) {
121       ExtractMethodPreviewManager.getInstance(myProject).closeContent(myContent);
122     }
123   }
124
125   private void doRefactor() {
126     if (myTree.isValid()) {
127       myDiffPanel.doExtract();
128       close();
129       return;
130     }
131     if (Messages.showYesNoDialog(myProject,
132                                  "Project files have been changed.\nWould you like to to re-run the refactoring?",
133                                  "Re-Run Refactoring", null) == Messages.YES) {
134       close();
135       myDiffPanel.tryExtractAgain();
136     }
137   }
138
139   private void rerunRefactoring() {
140     close();
141     myDiffPanel.tryExtractAgain();
142   }
143
144   void onTreeUpdated() {
145     myTree.repaint();
146     myDiffPanel.updateLater();
147   }
148
149   private void updateLater() {
150     myUpdateAlarm.cancelAllRequests();
151     myUpdateAlarm.addRequest(() -> {
152       if (myProject.isDisposed()) return;
153       PsiDocumentManagerBase documentManager = (PsiDocumentManagerBase)PsiDocumentManager.getInstance(myProject);
154       documentManager.cancelAndRunWhenAllCommitted("ExtractMethodPreview", this::updateImmediately);
155     }, 300);
156   }
157
158   private void updateImmediately() {
159     ApplicationManager.getApplication().assertIsDispatchThread();
160     if (myProject.isDisposed()) return;
161
162     boolean isModified = myDiffPanel.isModified();
163     if (myButtonsPanel.updateButtons(isModified)) {
164       myTree.setValid(!isModified);
165     }
166   }
167
168   private class ButtonsPanel extends JPanel {
169     private final JButton myRefactorButton;
170     private final JButton myRerunButton;
171     private final JButton myCancelButton;
172     private final Project myProject;
173     private boolean myModified; // Accessed in EDT
174
175     ButtonsPanel(@NotNull Project project) {
176       super(new FlowLayout(FlowLayout.LEFT, JBUI.scale(8), 0));
177       myProject = project;
178
179       myRefactorButton = new JButton(RefactoringBundle.message("refactoring.extract.method.preview.button.refactor"));
180       DialogUtil.registerMnemonic(myRefactorButton);
181       myRefactorButton.addActionListener(e -> doRefactor());
182       add(myRefactorButton);
183
184       myRerunButton = new JButton(RefactoringBundle.message("refactoring.extract.method.preview.button.rerun"));
185       DialogUtil.registerMnemonic(myRefactorButton);
186       myRerunButton.addActionListener(e -> rerunRefactoring());
187       add(myRerunButton);
188
189       myCancelButton = new JButton(IdeBundle.message("button.cancel"));
190       DialogUtil.registerMnemonic(myRefactorButton);
191       myCancelButton.addActionListener(e -> close());
192       add(myCancelButton);
193
194       updateButtonsImpl(false, false);
195
196       project.getMessageBus().connect(PreviewPanel.this).subscribe(DumbService.DUMB_MODE, new DumbService.DumbModeListener() {
197         @Override
198         public void enteredDumbMode() {
199           updateButtonsLater(true);
200         }
201
202         @Override
203         public void exitDumbMode() {
204           updateButtonsLater(false);
205         }
206       });
207     }
208
209     boolean updateButtons(boolean isModified) {
210       if (myModified == isModified) {
211         return false;
212       }
213       myModified = isModified;
214       updateButtonsImpl(DumbService.isDumb(myProject), isModified);
215       return true;
216     }
217
218     void updateButtonsLater(boolean isDumb) {
219       ApplicationManager.getApplication().invokeLater(() -> updateButtonsImpl(isDumb, myModified));
220     }
221
222     private void updateButtonsImpl(boolean isDumb, boolean isModified) {
223       myRefactorButton.setEnabled(!isDumb && !isModified);
224       myRefactorButton.setVisible(!isModified);
225       myRerunButton.setEnabled(!isDumb && isModified);
226       myRerunButton.setVisible(isModified);
227     }
228   }
229 }