2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com
.intellij
.ide
.util
.gotoByName
;
19 import com
.intellij
.featureStatistics
.FeatureUsageTracker
;
20 import com
.intellij
.openapi
.application
.ModalityState
;
21 import com
.intellij
.openapi
.project
.Project
;
22 import com
.intellij
.openapi
.util
.Key
;
23 import com
.intellij
.openapi
.util
.text
.StringUtil
;
24 import com
.intellij
.openapi
.wm
.ex
.LayoutFocusTraversalPolicyExt
;
25 import com
.intellij
.psi
.PsiElement
;
26 import com
.intellij
.psi
.statistics
.StatisticsInfo
;
27 import com
.intellij
.psi
.statistics
.StatisticsManager
;
28 import org
.jetbrains
.annotations
.Nullable
;
32 import java
.util
.List
;
33 import java
.util
.regex
.Matcher
;
34 import java
.util
.regex
.Pattern
;
36 public class ChooseByNamePopup
extends ChooseByNameBase
implements ChooseByNamePopupComponent
{
37 private static final Key
<ChooseByNamePopup
> CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY
= new Key
<ChooseByNamePopup
>("ChooseByNamePopup");
38 private Component myOldFocusOwner
= null;
40 private ChooseByNamePopup(final Project project
, final ChooseByNameModel model
, final ChooseByNamePopup oldPopup
,
41 final PsiElement context
, @Nullable final String predefinedText
) {
42 super(project
, model
, oldPopup
!= null ? oldPopup
.getEnteredText() : predefinedText
, context
);
43 if (oldPopup
!= null) { //inherit old focus owner
44 myOldFocusOwner
= oldPopup
.myPreviouslyFocusedComponent
;
48 public String
getEnteredText() {
49 return myTextField
.getText();
52 protected void initUI(final Callback callback
, final ModalityState modalityState
, boolean allowMultipleSelection
) {
53 super.initUI(callback
, modalityState
, allowMultipleSelection
);
54 //LaterInvocator.enterModal(myTextFieldPanel);
55 if (myInitialText
!= null) {
56 rebuildList(0, 0, null, ModalityState
.current());
58 if (myOldFocusOwner
!= null){
59 myPreviouslyFocusedComponent
= myOldFocusOwner
;
60 myOldFocusOwner
= null;
64 protected boolean isCheckboxVisible() {
68 protected boolean isShowListForEmptyPattern(){
72 protected boolean isCloseByFocusLost(){
76 protected void showList() {
77 final JLayeredPane layeredPane
= myTextField
.getRootPane().getLayeredPane();
78 final Rectangle bounds
= myTextFieldPanel
.getBounds();
79 bounds
.y
+= myTextFieldPanel
.getHeight();
80 final Dimension preferredScrollPaneSize
= myListScrollPane
.getPreferredSize();
81 preferredScrollPaneSize
.width
= Math
.max(myTextFieldPanel
.getWidth(), preferredScrollPaneSize
.width
);
82 if (bounds
.y
+ preferredScrollPaneSize
.height
> layeredPane
.getHeight()){ // clip scroll pane
83 preferredScrollPaneSize
.height
= layeredPane
.getHeight() - bounds
.y
;
86 if (preferredScrollPaneSize
.width
> layeredPane
.getWidth() - bounds
.x
) {
87 bounds
.x
= layeredPane
.getX() + Math
.max(1, layeredPane
.getWidth() - preferredScrollPaneSize
.width
);
88 if (preferredScrollPaneSize
.width
> layeredPane
.getWidth() - bounds
.x
) {
89 preferredScrollPaneSize
.width
= layeredPane
.getWidth() - bounds
.x
;
90 final JScrollBar horizontalScrollBar
= myListScrollPane
.getHorizontalScrollBar();
91 if (horizontalScrollBar
!= null){
92 preferredScrollPaneSize
.height
+= horizontalScrollBar
.getPreferredSize().getHeight();
97 Rectangle prefferedBounds
= new Rectangle(bounds
.x
, bounds
.y
, preferredScrollPaneSize
.width
, preferredScrollPaneSize
.height
);
99 if (myListScrollPane
.isVisible()) {
100 myListScrollPane
.setBounds(prefferedBounds
);
103 layeredPane
.add(myListScrollPane
, Integer
.valueOf(600));
104 layeredPane
.moveToFront(myListScrollPane
);
105 myListScrollPane
.validate();
106 myListScrollPane
.setVisible(true);
109 protected void hideList() {
110 if (myListScrollPane
.isVisible()){
111 myListScrollPane
.setVisible(false);
115 protected void close(final boolean isOk
) {
121 myModel
.saveInitialCheckBoxState(myCheckBox
.isSelected());
123 final List
<Object
> chosenElements
= getChosenElements();
124 if (chosenElements
!= null) {
125 for (Object element
: chosenElements
) {
126 myActionListener
.elementChosen(element
);
127 String text
= myModel
.getFullName(element
);
129 StatisticsManager
.getInstance().incUseCount(new StatisticsInfo(statisticsContext(), text
));
136 if (!chosenElements
.isEmpty()){
137 final String enteredText
= getEnteredText();
138 if (enteredText
.indexOf('*') >= 0) {
139 FeatureUsageTracker
.getInstance().triggerFeatureUsed("navigation.popup.wildcards");
142 for (Object element
: chosenElements
) {
143 final String name
= myModel
.getElementName(element
);
145 if (!StringUtil
.startsWithIgnoreCase(name
, enteredText
)) {
146 FeatureUsageTracker
.getInstance().triggerFeatureUsed("navigation.popup.camelprefix");
158 myDisposedFlag
= true;
159 myAlarm
.cancelAllRequests();
160 myProject
.putUserData(CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY
, null);
162 //LaterInvocator.leaveModal(myTextFieldPanel);
165 myActionListener
.onClose ();
168 private void cleanupUI() {
169 JLayeredPane layeredPane
= null;
171 // check if the currently focused component was changed already, so we could leave focus intact
172 final Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
173 if (owner
!= null && SwingUtilities
.isDescendingFrom(owner
, myTextField
)) {
174 // Return focus back to the previous focused component if we need to do it and
175 // previous focused component is showing.
177 myPreviouslyFocusedComponent
instanceof JComponent
&&
178 myPreviouslyFocusedComponent
.isShowing()
180 final JComponent _component
= (JComponent
)myPreviouslyFocusedComponent
;
181 LayoutFocusTraversalPolicyExt
.setOverridenDefaultComponent(_component
);
183 if (myPreviouslyFocusedComponent
!= null) {
184 myPreviouslyFocusedComponent
.requestFocus();
188 final JRootPane rootPane
= myTextFieldPanel
.getRootPane();
189 if (rootPane
!= null) {
190 layeredPane
= rootPane
.getLayeredPane();
191 layeredPane
.remove(myListScrollPane
);
192 layeredPane
.remove(myTextFieldPanel
);
196 LayoutFocusTraversalPolicyExt
.setOverridenDefaultComponent(null);
199 if (layeredPane
!= null) {
200 layeredPane
.validate();
201 layeredPane
.repaint();
205 public static ChooseByNamePopup
createPopup(final Project project
, final ChooseByNameModel model
, final PsiElement context
) {
206 return createPopup(project
, model
, context
, null);
208 public static ChooseByNamePopup
createPopup(final Project project
, final ChooseByNameModel model
, final PsiElement context
,
209 @Nullable final String predefinedText
) {
210 final ChooseByNamePopup oldPopup
= project
.getUserData(CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY
);
211 if (oldPopup
!= null) {
212 oldPopup
.close(false);
214 ChooseByNamePopup newPopup
= new ChooseByNamePopup(project
, model
, oldPopup
, context
, predefinedText
);
216 project
.putUserData(CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY
, newPopup
);
220 private static final Pattern patternToDetectLinesAndColumns
= Pattern
.compile("(.*?)(?:\\:|@|,|#)(\\d+)?(?:(?:\\D)(\\d+)?)?");
222 public String
getNamePattern(String pattern
) {
223 if (pattern
.indexOf(':') != -1 ||
224 pattern
.indexOf(',') != -1 ||
225 pattern
.indexOf(';') != -1 ||
226 pattern
.indexOf('#') != -1 ||
227 pattern
.indexOf('@') != -1) { // quick test if reg exp should be used
228 final Matcher matcher
= patternToDetectLinesAndColumns
.matcher(pattern
);
229 if (matcher
.matches()) {
230 pattern
= matcher
.group(1);
234 return super.getNamePattern(pattern
);
237 public int getLinePosition() {
238 return getLineOrColumn(true);
241 private int getLineOrColumn(final boolean line
) {
242 final Matcher matcher
= patternToDetectLinesAndColumns
.matcher(getEnteredText());
243 if (matcher
.matches()) {
244 final int groupNumber
= line ?
2:3;
246 if(groupNumber
<= matcher
.groupCount()) {
247 final String group
= matcher
.group(groupNumber
);
248 if (group
!= null) return Integer
.parseInt(group
) - 1;
250 if (!line
&& getLineOrColumn(true) != -1) return 0;
252 catch (NumberFormatException ignored
) {}
258 public int getColumnPosition() {
259 return getLineOrColumn(false);