IDEADEV-36399
[fedora-idea.git] / platform-impl / src / com / intellij / ui / popup / list / ListPopupImpl.java
blob9ea53e9321d1ed22ea950639ef001d3d9876de14
1 /*
2 * Copyright (c) 2000-2004 by JetBrains s.r.o. All Rights Reserved.
3 * Use is subject to license terms.
4 */
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;
19 import javax.swing.*;
20 import javax.swing.event.ListSelectionListener;
21 import java.awt.*;
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) {
39 super(aStep);
40 if (maxRowCount != -1){
41 myMaxRowCount = maxRowCount;
45 public ListPopupImpl(ListPopupStep aStep) {
46 super(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() {
63 return myListModel;
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;
78 return shouldShow;
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);
90 else {
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);
102 return false;
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);
114 final int count =
115 StatisticsManager.getInstance().getUseCount(new StatisticsInfo("#list_popup:" + myStep.getTitle() + "#" + filter, text));
116 if (count > maxUseCount) {
117 maxUseCount = count;
118 mostUsedValue = i;
122 if (mostUsedValue > 0) {
123 ListScrollingUtil.selectItem(myList, mostUsedValue);
124 return true;
128 return false;
131 private void selectFirstSelectableItem() {
132 for (int i = 0; i < myListModel.getSize(); i++) {
133 if (getListStep().isSelectable(myListModel.getElementAt(i))) {
134 myList.setSelectedIndex(i);
135 break;
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) ) {
146 countSelectables ++;
147 if (getStep().hasSubstep(elementAt)) {
148 if (oneSubmenuFound) {
149 return false;
151 oneSubmenuFound = true;
155 return oneSubmenuFound && countSelectables == 1;
158 private int getSelectableCount() {
159 int count = 0;
160 for (int i = 0; i < myListModel.getSize(); i++) {
161 final Object each = myListModel.getElementAt(i);
162 if (getListStep().isSelectable(each)) {
163 count++;
167 return count;
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) {
193 handleSelect(true);
197 registerAction("handleSelection2", KeyEvent.VK_RIGHT, 0, new AbstractAction() {
198 public void actionPerformed(ActionEvent e) {
199 handleSelect(false);
203 registerAction("goBack2", KeyEvent.VK_LEFT, 0, new AbstractAction() {
204 public void actionPerformed(ActionEvent e) {
205 if (isClosableByLeftArrow()) {
206 goBack();
212 myList.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
214 return myList;
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);
240 super.dispose();
243 public void disposeChildren() {
244 setIndexForShowingChild(-1);
245 super.disposeChildren();
248 protected void onAutoSelectionTimer() {
249 if (myList.getModel().getSize() > 0 && !myList.isSelectionEmpty() ) {
250 handleSelect(false);
252 else {
253 disposeChildren();
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()) {
272 return false;
275 final Object selectedValue = myList.getSelectedValue();
276 if (!getListStep().isSelectable(selectedValue)) return false;
278 if (!getListStep().hasSubstep(selectedValue) && !handleFinalChoices) return false;
280 disposeChildren();
282 if (myListModel.getSize() == 0) {
283 disposeAllParents(e);
284 setIndexForShowingChild(-1);
285 return true;
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());
315 return false;
317 else {
318 disposeAllParents(e);
319 setIndexForShowingChild(-1);
320 return true;
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);
335 restartTimer();
338 notifyParentOnChildSelection();
342 private class MyMouseListener extends MouseAdapter {
344 @Override
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);
360 stopTimer();
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 {
377 public MyList() {
378 super(myListModel);
381 public Dimension getPreferredScrollableViewportSize() {
382 return new Dimension(super.getPreferredScrollableViewportSize().width, getPreferredSize().height);
385 public void processKeyEvent(KeyEvent e) {
386 e.setSource(this);
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);
410 myList.repaint();
411 SwingUtilities.invokeLater(new Runnable() {
412 public void run() {
413 handleSelect(true);
419 protected JComponent getPreferredFocusableComponent() {
420 return myList;
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() {
434 return true;