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
.myTextField
.getText() : predefinedText
, context
);
43 if (oldPopup
!= null) { //inherit old focus owner
44 myOldFocusOwner
= oldPopup
.myPreviouslyFocusedComponent
;
48 protected void initUI(final Callback callback
, final ModalityState modalityState
, boolean allowMultipleSelection
) {
49 super.initUI(callback
, modalityState
, allowMultipleSelection
);
50 //LaterInvocator.enterModal(myTextFieldPanel);
51 if (myInitialText
!= null) {
52 rebuildList(0, 0, null, ModalityState
.current());
54 if (myOldFocusOwner
!= null){
55 myPreviouslyFocusedComponent
= myOldFocusOwner
;
56 myOldFocusOwner
= null;
60 protected boolean isCheckboxVisible() {
64 protected boolean isShowListForEmptyPattern(){
68 protected boolean isCloseByFocusLost(){
72 protected void showList() {
73 final JLayeredPane layeredPane
= myTextField
.getRootPane().getLayeredPane();
74 final Rectangle bounds
= myTextFieldPanel
.getBounds();
75 bounds
.y
+= myTextFieldPanel
.getHeight();
76 final Dimension preferredScrollPaneSize
= myListScrollPane
.getPreferredSize();
77 preferredScrollPaneSize
.width
= Math
.max(myTextFieldPanel
.getWidth(), preferredScrollPaneSize
.width
);
78 if (bounds
.y
+ preferredScrollPaneSize
.height
> layeredPane
.getHeight()){ // clip scroll pane
79 preferredScrollPaneSize
.height
= layeredPane
.getHeight() - bounds
.y
;
82 if (preferredScrollPaneSize
.width
> layeredPane
.getWidth() - bounds
.x
) {
83 bounds
.x
= layeredPane
.getX() + Math
.max(1, layeredPane
.getWidth() - preferredScrollPaneSize
.width
);
84 if (preferredScrollPaneSize
.width
> layeredPane
.getWidth() - bounds
.x
) {
85 preferredScrollPaneSize
.width
= layeredPane
.getWidth() - bounds
.x
;
86 final JScrollBar horizontalScrollBar
= myListScrollPane
.getHorizontalScrollBar();
87 if (horizontalScrollBar
!= null){
88 preferredScrollPaneSize
.height
+= horizontalScrollBar
.getPreferredSize().getHeight();
93 Rectangle prefferedBounds
= new Rectangle(bounds
.x
, bounds
.y
, preferredScrollPaneSize
.width
, preferredScrollPaneSize
.height
);
95 if (myListScrollPane
.isVisible()) {
96 myListScrollPane
.setBounds(prefferedBounds
);
99 layeredPane
.add(myListScrollPane
, Integer
.valueOf(600));
100 layeredPane
.moveToFront(myListScrollPane
);
101 myListScrollPane
.validate();
102 myListScrollPane
.setVisible(true);
105 protected void hideList() {
106 if (myListScrollPane
.isVisible()){
107 myListScrollPane
.setVisible(false);
111 protected void close(final boolean isOk
) {
117 myModel
.saveInitialCheckBoxState(myCheckBox
.isSelected());
119 final List
<Object
> chosenElements
= getChosenElements();
120 if (chosenElements
!= null) {
121 for (Object element
: chosenElements
) {
122 myActionListener
.elementChosen(element
);
123 String text
= myModel
.getFullName(element
);
125 StatisticsManager
.getInstance().incUseCount(new StatisticsInfo(statisticsContext(), text
));
132 if (!chosenElements
.isEmpty()){
133 final String enteredText
= myTextField
.getText();
134 if (enteredText
.indexOf('*') >= 0) {
135 FeatureUsageTracker
.getInstance().triggerFeatureUsed("navigation.popup.wildcards");
138 for (Object element
: chosenElements
) {
139 final String name
= myModel
.getElementName(element
);
141 if (!StringUtil
.startsWithIgnoreCase(name
, enteredText
)) {
142 FeatureUsageTracker
.getInstance().triggerFeatureUsed("navigation.popup.camelprefix");
154 myDisposedFlag
= true;
155 myAlarm
.cancelAllRequests();
156 myProject
.putUserData(CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY
, null);
158 //LaterInvocator.leaveModal(myTextFieldPanel);
161 myActionListener
.onClose ();
164 private void cleanupUI() {
165 JLayeredPane layeredPane
= null;
167 // check if the currently focused component was changed already, so we could leave focus intact
168 final Component owner
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getFocusOwner();
169 if (owner
!= null && SwingUtilities
.isDescendingFrom(owner
, myTextField
)) {
170 // Return focus back to the previous focused component if we need to do it and
171 // previous focused component is showing.
173 myPreviouslyFocusedComponent
instanceof JComponent
&&
174 myPreviouslyFocusedComponent
.isShowing()
176 final JComponent _component
= (JComponent
)myPreviouslyFocusedComponent
;
177 LayoutFocusTraversalPolicyExt
.setOverridenDefaultComponent(_component
);
179 if (myPreviouslyFocusedComponent
!= null) {
180 myPreviouslyFocusedComponent
.requestFocus();
184 final JRootPane rootPane
= myTextFieldPanel
.getRootPane();
185 if (rootPane
!= null) {
186 layeredPane
= rootPane
.getLayeredPane();
187 layeredPane
.remove(myListScrollPane
);
188 layeredPane
.remove(myTextFieldPanel
);
192 LayoutFocusTraversalPolicyExt
.setOverridenDefaultComponent(null);
195 if (layeredPane
!= null) {
196 layeredPane
.validate();
197 layeredPane
.repaint();
201 public static ChooseByNamePopup
createPopup(final Project project
, final ChooseByNameModel model
, final PsiElement context
) {
202 return createPopup(project
, model
, context
, null);
204 public static ChooseByNamePopup
createPopup(final Project project
, final ChooseByNameModel model
, final PsiElement context
,
205 @Nullable final String predefinedText
) {
206 final ChooseByNamePopup oldPopup
= project
.getUserData(CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY
);
207 if (oldPopup
!= null) {
208 oldPopup
.close(false);
210 ChooseByNamePopup newPopup
= new ChooseByNamePopup(project
, model
, oldPopup
, context
, predefinedText
);
212 project
.putUserData(CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY
, newPopup
);
216 private static final Pattern patternToDetectLinesAndColumns
= Pattern
.compile("(.*?)(?:\\:|@|,|#)(\\d+)?(?:(?:\\D)(\\d+)?)?");
218 public String
getNamePattern(String pattern
) {
219 if (pattern
.indexOf(':') != -1 ||
220 pattern
.indexOf(',') != -1 ||
221 pattern
.indexOf(';') != -1 ||
222 pattern
.indexOf('#') != -1 ||
223 pattern
.indexOf('@') != -1) { // quick test if reg exp should be used
224 final Matcher matcher
= patternToDetectLinesAndColumns
.matcher(pattern
);
225 if (matcher
.matches()) {
226 pattern
= matcher
.group(1);
230 return super.getNamePattern(pattern
);
233 public int getLinePosition() {
234 return getLineOrColumn(true);
237 private int getLineOrColumn(final boolean line
) {
238 final Matcher matcher
= patternToDetectLinesAndColumns
.matcher(myTextField
.getText());
239 if (matcher
.matches()) {
240 final int groupNumber
= line ?
2:3;
242 if(groupNumber
<= matcher
.groupCount()) {
243 final String group
= matcher
.group(groupNumber
);
244 if (group
!= null) return Integer
.parseInt(group
) - 1;
246 if (!line
&& getLineOrColumn(true) != -1) return 0;
248 catch (NumberFormatException ignored
) {}
254 public int getColumnPosition() {
255 return getLineOrColumn(false);