fe80dc840697bc808fd5ff2dcc9824b0c4838f13
[idea/community.git] / platform / lang-impl / src / com / intellij / refactoring / introduce / inplace / InplaceVariableIntroducer.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.refactoring.introduce.inplace;
17
18 import com.intellij.codeInsight.lookup.LookupElement;
19 import com.intellij.codeInsight.lookup.LookupElementBuilder;
20 import com.intellij.codeInsight.template.TextResult;
21 import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
22 import com.intellij.codeInsight.template.impl.TemplateState;
23 import com.intellij.openapi.Disposable;
24 import com.intellij.openapi.application.ApplicationManager;
25 import com.intellij.openapi.command.impl.StartMarkAction;
26 import com.intellij.openapi.editor.Editor;
27 import com.intellij.openapi.editor.RangeMarker;
28 import com.intellij.openapi.extensions.Extensions;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.ui.popup.Balloon;
31 import com.intellij.openapi.ui.popup.BalloonBuilder;
32 import com.intellij.openapi.ui.popup.JBPopupFactory;
33 import com.intellij.openapi.util.*;
34 import com.intellij.psi.PsiElement;
35 import com.intellij.psi.PsiNamedElement;
36 import com.intellij.refactoring.rename.NameSuggestionProvider;
37 import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
38 import com.intellij.ui.awt.RelativePoint;
39 import com.intellij.util.ui.PositionTracker;
40 import org.jetbrains.annotations.Nullable;
41
42 import javax.swing.*;
43 import java.awt.*;
44 import java.util.ArrayList;
45 import java.util.Iterator;
46 import java.util.LinkedHashSet;
47 import java.util.List;
48
49 /**
50  * User: anna
51  * Date: 3/15/11
52  */
53 public abstract class InplaceVariableIntroducer<E extends PsiElement> extends InplaceRefactoring {
54   public static final Key<Boolean> INTRODUCE_RESTART = Key.create("INTRODUCE_RESTART");
55
56   protected E myExpr;
57   protected RangeMarker myExprMarker;
58
59   protected E[] myOccurrences;
60   protected List<RangeMarker> myOccurrenceMarkers;
61
62   protected Balloon myBalloon;
63   protected String myTitle;
64   protected RelativePoint myTarget;
65   private RangeMarker myCaretRangeMarker;
66
67   public InplaceVariableIntroducer(PsiNamedElement elementToRename,
68                                    Editor editor,
69                                    Project project,
70                                    String title, E[] occurrences, 
71                                    @Nullable E expr) {
72     super(editor, elementToRename, project);
73     myTitle = title;
74     myOccurrences = occurrences;
75     myExpr = expr;
76     myExprMarker = myExpr != null && myExpr.isPhysical() ? createMarker(myExpr) : null;
77     initOccurrencesMarkers();
78   }
79
80   @Deprecated
81   public boolean performInplaceRename() {
82     return performInplaceRefactoring(null);
83   }
84   
85   @Override
86   protected boolean shouldSelectAll() {
87     return true;
88   }
89
90   @Override
91   protected int restoreCaretOffset(int offset) {
92     return myCaretRangeMarker.isValid() ? myCaretRangeMarker.getStartOffset() : offset;
93   }
94
95   @Override
96   protected StartMarkAction startRename() throws StartMarkAction.AlreadyStartedException {
97     return null;
98   }
99
100   @Nullable
101   protected JComponent getComponent() {
102     return null;
103   }
104
105   public void setOccurrenceMarkers(List<RangeMarker> occurrenceMarkers) {
106     myOccurrenceMarkers = occurrenceMarkers;
107   }
108
109   public void setExprMarker(RangeMarker exprMarker) {
110     myExprMarker = exprMarker;
111   }
112
113   public E getExpr() {
114     return myExpr != null && myExpr.isValid() && myExpr.isPhysical() ? myExpr : null;
115   }
116
117   public E[] getOccurrences() {
118     return myOccurrences;
119   }
120
121   public List<RangeMarker> getOccurrenceMarkers() {
122     if (myOccurrenceMarkers == null) {
123       initOccurrencesMarkers();
124     }
125     return myOccurrenceMarkers;
126   }
127
128   protected void initOccurrencesMarkers() {
129     if (myOccurrenceMarkers != null) return;
130     myOccurrenceMarkers = new ArrayList<RangeMarker>();
131     for (E occurrence : myOccurrences) {
132       myOccurrenceMarkers.add(createMarker(occurrence));
133     }
134   }
135
136   protected RangeMarker createMarker(PsiElement element) {
137     return myEditor.getDocument().createRangeMarker(element.getTextRange());
138   }
139
140
141   public RangeMarker getExprMarker() {
142     return myExprMarker;
143   }
144
145   @Override
146   protected boolean performRefactoring() {
147     return false;
148   }
149
150   @Override
151   protected void beforeTemplateStart() {
152     myCaretRangeMarker = myEditor.getDocument()
153       .createRangeMarker(new TextRange(myEditor.getCaretModel().getOffset(), myEditor.getCaretModel().getOffset()));
154   }
155
156   @Override
157   protected void collectAdditionalElementsToRename(List<Pair<PsiElement, TextRange>> stringUsages) {
158   }
159
160   @Override
161   protected String getCommandName() {
162     return myTitle;
163   }
164
165   @Override
166   public boolean performInplaceRefactoring(LinkedHashSet<String> nameSuggestions) {
167     final boolean result = super.performInplaceRefactoring(nameSuggestions);
168     if (result) {
169       if (myBalloon == null) {
170         showBalloon();
171       }
172     }
173     return result;
174   }
175
176   @Override
177   protected void moveOffsetAfter(boolean success) {
178     super.moveOffsetAfter(success);
179     if (myOccurrenceMarkers != null) {
180       for (RangeMarker marker : myOccurrenceMarkers) {
181         marker.dispose();
182       }
183     }
184     if (myCaretRangeMarker != null) {
185       myCaretRangeMarker.dispose();
186     }
187     if (myExprMarker != null && !isRestart()) {
188       myExprMarker.dispose();
189     }
190   }
191
192   protected boolean isRestart() {
193     final Boolean isRestart = myEditor.getUserData(INTRODUCE_RESTART);
194     return isRestart != null && isRestart;
195   }
196   
197   protected void releaseResources() {
198   }
199
200   protected void showBalloon() {
201     final JComponent component = getComponent();
202     if (component == null) return;
203     if (ApplicationManager.getApplication().isHeadlessEnvironment()) return;
204     final BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createDialogBalloonBuilder(component, null).setSmallVariant(true);
205     myBalloon = balloonBuilder.createBalloon();
206     Disposer.register(myProject, myBalloon);
207     Disposer.register(myBalloon, new Disposable() {
208       @Override
209       public void dispose() {
210         releaseIfNotRestart();
211       }
212     });
213     myBalloon.show(new PositionTracker<Balloon>(myEditor.getContentComponent()) {
214       @Override
215       public RelativePoint recalculateLocation(Balloon object) {
216         final RelativePoint target = JBPopupFactory.getInstance().guessBestPopupLocation(myEditor);
217         final Point screenPoint = target.getScreenPoint();
218         int y = screenPoint.y;
219         if (target.getPoint().getY() > myEditor.getLineHeight() + myBalloon.getPreferredSize().getHeight()) {
220           y -= myEditor.getLineHeight();
221         }
222         myTarget = new RelativePoint(new Point(screenPoint.x, y));
223         return myTarget;
224       }
225     }, Balloon.Position.above);
226   }
227
228   protected void releaseIfNotRestart() {
229     final Boolean isRestart = myEditor.getUserData(INTRODUCE_RESTART);
230     if (isRestart == null || !isRestart.booleanValue()) {
231       releaseResources();
232     }
233   }
234
235   @Override
236   public void finish() {
237     super.finish();
238     if (myBalloon != null) {
239       final Boolean isRestart = myEditor.getUserData(INTRODUCE_RESTART);
240       if (isRestart == null || !isRestart.booleanValue()) {
241         myBalloon.hide();
242       }
243     }
244   }
245
246   @Override
247   protected LookupElement[] createLookupItems(LookupElement[] lookupItems, String name) {
248     TemplateState templateState = TemplateManagerImpl.getTemplateState(myEditor);
249     final PsiNamedElement psiVariable = getVariable();
250     if (psiVariable != null) {
251       final TextResult insertedValue =
252         templateState != null ? templateState.getVariableValue(PRIMARY_VARIABLE_NAME) : null;
253       if (insertedValue != null) {
254         final String text = insertedValue.getText();
255         if (!text.isEmpty() && !Comparing.strEqual(text, name)) {
256           final LinkedHashSet<String> names = new LinkedHashSet<String>();
257           names.add(text);
258           for (NameSuggestionProvider provider : Extensions.getExtensions(NameSuggestionProvider.EP_NAME)) {
259             provider.getSuggestedNames(psiVariable, psiVariable, names);
260           }
261           final LookupElement[] items = new LookupElement[names.size()];
262           final Iterator<String> iterator = names.iterator();
263           for (int i = 0; i < items.length; i++) {
264             items[i] = LookupElementBuilder.create(iterator.next());
265           }
266           return items;
267         }
268       }
269     }
270     return lookupItems;
271   }
272
273 }