2 * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
3 * Use is subject to license terms.
5 package com
.intellij
.ui
.popup
.list
;
7 import com
.intellij
.openapi
.ui
.popup
.ListPopup
;
8 import com
.intellij
.openapi
.ui
.popup
.ListPopupStep
;
9 import com
.intellij
.openapi
.ui
.popup
.PopupStep
;
10 import com
.intellij
.openapi
.util
.text
.StringUtil
;
11 import com
.intellij
.psi
.statistics
.StatisticsInfo
;
12 import com
.intellij
.psi
.statistics
.StatisticsManager
;
13 import com
.intellij
.ui
.ListScrollingUtil
;
14 import com
.intellij
.ui
.popup
.ClosableByLeftArrow
;
15 import com
.intellij
.ui
.popup
.PopupIcons
;
16 import com
.intellij
.ui
.popup
.WizardPopup
;
17 import com
.intellij
.util
.ui
.UIUtil
;
20 import javax
.swing
.event
.ListSelectionListener
;
22 import java
.awt
.event
.*;
24 public class ListPopupImpl
extends WizardPopup
implements ListPopup
{
26 private MyList myList
;
28 private MyMouseMotionListener myMouseMotionListener
;
29 private MyMouseListener myMouseListener
;
31 private ListPopupModel myListModel
;
33 private int myIndexForShowingChild
= -1;
34 private int myMaxRowCount
= 20;
35 private boolean myAutoHandleBeforeShow
;
38 public ListPopupImpl(ListPopupStep aStep
, int maxRowCount
) {
40 if (maxRowCount
!= -1){
41 myMaxRowCount
= maxRowCount
;
45 public ListPopupImpl(ListPopupStep aStep
) {
49 public ListPopupImpl(WizardPopup aParent
, ListPopupStep aStep
, Object parentValue
) {
50 super(aParent
, aStep
);
51 setParentValue(parentValue
);
54 public ListPopupImpl(WizardPopup aParent
, ListPopupStep aStep
, Object parentValue
, int maxRowCount
) {
55 super(aParent
, aStep
);
56 setParentValue(parentValue
);
57 if (maxRowCount
!= -1){
58 myMaxRowCount
= maxRowCount
;
62 ListPopupModel
getListModel() {
66 protected boolean beforeShow() {
67 myList
.addMouseMotionListener(myMouseMotionListener
);
68 myList
.addMouseListener(myMouseListener
);
70 myList
.setVisibleRowCount(Math
.min(myMaxRowCount
, myListModel
.getSize()));
72 boolean shouldShow
= super.beforeShow();
73 if (myAutoHandleBeforeShow
) {
74 final boolean toDispose
= tryToAutoSelect(true);
75 shouldShow
&= !toDispose
;
81 protected void afterShow() {
82 tryToAutoSelect(false);
85 private boolean tryToAutoSelect(boolean handleFinalChoices
) {
86 final int defaultIndex
= getListStep().getDefaultOptionIndex();
87 if (defaultIndex
>= 0 && defaultIndex
< myList
.getModel().getSize()) {
88 ListScrollingUtil
.selectItem(myList
, defaultIndex
);
91 selectFirstSelectableItem();
94 if (getListStep().isAutoSelectionEnabled()) {
95 if (!isVisible() && getSelectableCount() == 1) {
96 return _handleSelect(handleFinalChoices
, null);
97 } else if (isVisible() && hasSingleSelectableItemWithSubmenu()) {
98 return _handleSelect(handleFinalChoices
, null);
105 private boolean autoSelectUsingStatistics() {
106 final String filter
= getSpeedSearch().getFilter();
107 if (!StringUtil
.isEmpty(filter
)) {
108 int maxUseCount
= -1;
109 int mostUsedValue
= -1;
110 int elementsCount
= myListModel
.getSize();
111 for (int i
= 0; i
< elementsCount
; i
++) {
112 Object value
= myListModel
.getElementAt(i
);
113 final String text
= getListStep().getTextFor(value
);
115 StatisticsManager
.getInstance().getUseCount(new StatisticsInfo("#list_popup:" + myStep
.getTitle() + "#" + filter
, text
));
116 if (count
> maxUseCount
) {
122 if (mostUsedValue
> 0) {
123 ListScrollingUtil
.selectItem(myList
, mostUsedValue
);
131 private void selectFirstSelectableItem() {
132 for (int i
= 0; i
< myListModel
.getSize(); i
++) {
133 if (getListStep().isSelectable(myListModel
.getElementAt(i
))) {
134 myList
.setSelectedIndex(i
);
140 private boolean hasSingleSelectableItemWithSubmenu() {
141 boolean oneSubmenuFound
= false;
142 int countSelectables
= 0;
143 for (int i
= 0; i
< myListModel
.getSize(); i
++) {
144 Object elementAt
= myListModel
.getElementAt(i
);
145 if (getListStep().isSelectable(elementAt
) ) {
147 if (getStep().hasSubstep(elementAt
)) {
148 if (oneSubmenuFound
) {
151 oneSubmenuFound
= true;
155 return oneSubmenuFound
&& countSelectables
== 1;
158 private int getSelectableCount() {
160 for (int i
= 0; i
< myListModel
.getSize(); i
++) {
161 final Object each
= myListModel
.getElementAt(i
);
162 if (getListStep().isSelectable(each
)) {
170 protected JComponent
createContent() {
171 myMouseMotionListener
= new MyMouseMotionListener();
172 myMouseListener
= new MyMouseListener();
174 myListModel
= new ListPopupModel(this, getSpeedSearch(), getListStep());
175 myList
= new MyList();
176 if (myStep
.getTitle() != null) {
177 myList
.getAccessibleContext().setAccessibleName(myStep
.getTitle());
179 myList
.setSelectionMode(ListSelectionModel
.SINGLE_SELECTION
);
181 myList
.setSelectedIndex(0);
182 myList
.setBorder(BorderFactory
.createEmptyBorder(5, 5, 5, 5));
184 ListScrollingUtil
.installActions(myList
);
186 myList
.setCellRenderer(getListElementRenderer());
188 myList
.getActionMap().get("selectNextColumn").setEnabled(false);
189 myList
.getActionMap().get("selectPreviousColumn").setEnabled(false);
191 registerAction("handleSelection1", KeyEvent
.VK_ENTER
, 0, new AbstractAction() {
192 public void actionPerformed(ActionEvent e
) {
197 registerAction("handleSelection2", KeyEvent
.VK_RIGHT
, 0, new AbstractAction() {
198 public void actionPerformed(ActionEvent e
) {
203 registerAction("goBack2", KeyEvent
.VK_LEFT
, 0, new AbstractAction() {
204 public void actionPerformed(ActionEvent e
) {
205 if (isClosableByLeftArrow()) {
212 myList
.setCursor(Cursor
.getPredefinedCursor(Cursor
.HAND_CURSOR
));
217 private boolean isClosableByLeftArrow() {
218 return getParent() != null || myStep
instanceof ClosableByLeftArrow
;
221 protected ActionMap
getActionMap() {
222 return myList
.getActionMap();
225 protected InputMap
getInputMap() {
226 return myList
.getInputMap();
229 protected ListCellRenderer
getListElementRenderer() {
230 return new PopupListElementRenderer(this);
233 public ListPopupStep
<Object
> getListStep() {
234 return (ListPopupStep
<Object
>) myStep
;
237 public void dispose() {
238 myList
.removeMouseMotionListener(myMouseMotionListener
);
239 myList
.removeMouseListener(myMouseListener
);
243 public void disposeChildren() {
244 setIndexForShowingChild(-1);
245 super.disposeChildren();
248 protected void onAutoSelectionTimer() {
249 if (myList
.getModel().getSize() > 0 && !myList
.isSelectionEmpty() ) {
254 setIndexForShowingChild(-1);
258 public void handleSelect(boolean handleFinalChoices
) {
259 _handleSelect(handleFinalChoices
, null);
262 public void handleSelect(boolean handleFinalChoices
, InputEvent e
) {
263 _handleSelect(handleFinalChoices
, e
);
266 private boolean _handleSelect(final boolean handleFinalChoices
, InputEvent e
) {
267 if (myList
.getSelectedIndex() == -1) return false;
269 if (getSpeedSearch().isHoldingFilter() && myList
.getModel().getSize() == 0) return false;
271 if (myList
.getSelectedIndex() == getIndexForShowingChild()) {
275 final Object selectedValue
= myList
.getSelectedValue();
276 if (!getListStep().isSelectable(selectedValue
)) return false;
278 if (!getListStep().hasSubstep(selectedValue
) && !handleFinalChoices
) return false;
282 if (myListModel
.getSize() == 0) {
283 disposeAllParents(e
);
284 setIndexForShowingChild(-1);
288 valueSelected(selectedValue
);
290 return handleNextStep(myStep
.onChosen(selectedValue
, handleFinalChoices
), selectedValue
, e
);
293 private void valueSelected(final Object value
) {
294 final String filter
= getSpeedSearch().getFilter();
295 if (!StringUtil
.isEmpty(filter
)) {
296 final String text
= getListStep().getTextFor(value
);
297 StatisticsManager
.getInstance().incUseCount(new StatisticsInfo("#list_popup:" + getListStep().getTitle() + "#" + filter
, text
));
301 private boolean handleNextStep(final PopupStep nextStep
, Object parentValue
, InputEvent e
) {
302 if (nextStep
!= PopupStep
.FINAL_CHOICE
) {
303 final Point point
= myList
.indexToLocation(myList
.getSelectedIndex());
304 SwingUtilities
.convertPointToScreen(point
, myList
);
305 myChild
= createPopup(this, nextStep
, parentValue
);
306 if (myChild
instanceof ListPopupImpl
) {
307 for (ListSelectionListener listener
: myList
.getListSelectionListeners()) {
308 ((ListPopupImpl
)myChild
).addListSelectionListener(listener
);
311 final JComponent container
= getContent();
312 assert container
!= null : "container == null";
313 myChild
.show(container
, point
.x
+ container
.getWidth() - STEP_X_PADDING
, point
.y
, true);
314 setIndexForShowingChild(myList
.getSelectedIndex());
318 disposeAllParents(e
);
319 setIndexForShowingChild(-1);
324 public void addListSelectionListener(ListSelectionListener listSelectionListener
) {
325 myList
.addListSelectionListener(listSelectionListener
);
328 private class MyMouseMotionListener
extends MouseMotionAdapter
{
329 public void mouseMoved(MouseEvent e
) {
330 Point point
= e
.getPoint();
331 int index
= myList
.locationToIndex(point
);
333 if (index
!= myList
.getSelectedIndex()) {
334 myList
.setSelectedIndex(index
);
338 notifyParentOnChildSelection();
342 private class MyMouseListener
extends MouseAdapter
{
345 public void mousePressed(MouseEvent e
) {
346 if (!UIUtil
.isActionClick(e
)) return;
348 boolean handleFinalChoices
= true;
349 final Object selectedValue
= myList
.getSelectedValue();
350 final ListPopupStep
<Object
> listStep
= getListStep();
351 if (selectedValue
!= null && listStep
.hasSubstep(selectedValue
) && listStep
.isSelectable(selectedValue
)) {
352 final int index
= myList
.getSelectedIndex();
353 final Rectangle bounds
= myList
.getCellBounds(index
, index
);
354 final Point point
= e
.getPoint();
355 if (point
.getX() > bounds
.width
+ bounds
.getX() - PopupIcons
.HAS_NEXT_ICON
.getIconWidth()) { //press on handle icon
356 handleFinalChoices
= false;
359 handleSelect(handleFinalChoices
, e
);
364 protected void process(KeyEvent aEvent
) {
365 myList
.processKeyEvent(aEvent
);
368 private int getIndexForShowingChild() {
369 return myIndexForShowingChild
;
372 private void setIndexForShowingChild(int aIndexForShowingChild
) {
373 myIndexForShowingChild
= aIndexForShowingChild
;
376 private class MyList
extends JList
{
381 public Dimension
getPreferredScrollableViewportSize() {
382 return new Dimension(super.getPreferredScrollableViewportSize().width
, getPreferredSize().height
);
385 public void processKeyEvent(KeyEvent e
) {
387 super.processKeyEvent(e
);
391 protected void onSpeedSearchPatternChanged() {
392 myListModel
.refilter();
393 if (myListModel
.getSize() > 0) {
394 if (!autoSelectUsingStatistics()) {
395 int fullMatchIndex
= myListModel
.getClosestMatchIndex();
396 if (fullMatchIndex
!= -1) {
397 myList
.setSelectedIndex(fullMatchIndex
);
400 if (myListModel
.getSize() <= myList
.getSelectedIndex() || !myListModel
.isVisible(myList
.getSelectedValue())) {
401 myList
.setSelectedIndex(0);
407 protected void onSelectByMnemonic(Object value
) {
408 if (myListModel
.isVisible(value
)) {
409 myList
.setSelectedValue(value
, true);
411 SwingUtilities
.invokeLater(new Runnable() {
419 protected JComponent
getPreferredFocusableComponent() {
423 protected void onChildSelectedFor(Object value
) {
424 if (myList
.getSelectedValue() != value
) {
425 myList
.setSelectedValue(value
, false);
429 public void setHandleAutoSelectionBeforeShow(final boolean autoHandle
) {
430 myAutoHandleBeforeShow
= autoHandle
;
433 public boolean isModalContext() {