popups: lost data context because of invaokeLater + made setFinalRunnable() public
[fedora-idea.git] / platform / platform-api / src / com / intellij / openapi / ui / popup / PopupChooserBuilder.java
blobe39dc903ed20ac32bbdb5a8e74ba063a5ec5f96a
1 /*
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.openapi.ui.popup;
19 import com.intellij.openapi.actionSystem.DataProvider;
20 import com.intellij.openapi.actionSystem.PlatformDataKeys;
21 import com.intellij.openapi.util.Computable;
22 import com.intellij.openapi.util.Pair;
23 import com.intellij.ui.InplaceButton;
24 import com.intellij.ui.ListScrollingUtil;
25 import com.intellij.ui.speedSearch.ListWithFilter;
26 import com.intellij.ui.treeStructure.treetable.TreeTable;
27 import com.intellij.util.Function;
28 import com.intellij.util.ui.UIUtil;
29 import com.intellij.util.ui.tree.TreeUtil;
30 import org.jetbrains.annotations.Nls;
31 import org.jetbrains.annotations.NonNls;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
35 import javax.swing.*;
36 import javax.swing.border.Border;
37 import java.awt.*;
38 import java.awt.event.*;
39 import java.util.ArrayList;
40 import java.util.List;
42 /**
43 * @author max
45 public class PopupChooserBuilder {
46 private JComponent myChooserComponent;
47 private String myTitle;
48 private final ArrayList<KeyStroke> myAdditionalKeystrokes = new ArrayList<KeyStroke>();
49 private Runnable myItemChoosenRunnable;
50 private JComponent mySouthComponent;
51 private JComponent myEastComponent;
53 private JBPopup myPopup;
55 private boolean myRequestFocus = true;
56 private boolean myForceResizable = false;
57 private boolean myForceMovable = false;
58 private String myDimensionServiceKey = null;
59 private Computable<Boolean> myCancelCallback;
60 private boolean myAutoselect = true;
61 private float myAlpha;
62 private Component[] myFocusOwners = new Component[0];
63 private boolean myCancelKeyEnabled = true;
65 ArrayList<JBPopupListener> myListeners = new ArrayList<JBPopupListener>();
66 private String myAd;
67 private Dimension myMinSize;
68 private InplaceButton myCommandButton;
69 private final List<Pair<ActionListener,KeyStroke>> myKeyboardActions = new ArrayList<Pair<ActionListener, KeyStroke>>();
70 private Component mySettingsButtons;
71 private boolean myAutoselectOnMouseMove = true;
73 private Function<Object,String> myItemsNamer = null;
75 public PopupChooserBuilder(@NotNull JList list) {
76 myChooserComponent = list;
79 public PopupChooserBuilder(@NotNull JTable table) {
80 myChooserComponent = table;
83 public PopupChooserBuilder(@NotNull JTree tree) {
84 myChooserComponent = tree;
87 @NotNull
88 public PopupChooserBuilder setTitle(@NotNull @Nls String title) {
89 myTitle = title;
90 return this;
93 @NotNull
94 public PopupChooserBuilder addAdditionalChooseKeystroke(@Nullable KeyStroke keyStroke) {
95 if (keyStroke != null) {
96 myAdditionalKeystrokes.add(keyStroke);
98 return this;
101 @NotNull
102 public PopupChooserBuilder setItemChoosenCallback(@NotNull Runnable runnable) {
103 myItemChoosenRunnable = runnable;
104 return this;
107 @NotNull
108 public PopupChooserBuilder setSouthComponent(@NotNull JComponent cmp) {
109 mySouthComponent = cmp;
110 return this;
113 @NotNull
114 public PopupChooserBuilder setEastComponent(@NotNull JComponent cmp) {
115 myEastComponent = cmp;
116 return this;
120 public PopupChooserBuilder setRequestFocus(final boolean requestFocus) {
121 myRequestFocus = requestFocus;
122 return this;
125 public PopupChooserBuilder setResizable(final boolean forceResizable) {
126 myForceResizable = forceResizable;
127 return this;
131 public PopupChooserBuilder setMovable(final boolean forceMovable) {
132 myForceMovable = forceMovable;
133 return this;
136 public PopupChooserBuilder setDimensionServiceKey(@NonNls String key){
137 myDimensionServiceKey = key;
138 return this;
141 public PopupChooserBuilder setCancelCallback(Computable<Boolean> callback) {
142 myCancelCallback = callback;
143 return this;
146 public PopupChooserBuilder setCommandButton(@NotNull InplaceButton commandButton) {
147 myCommandButton = commandButton;
148 return this;
151 public PopupChooserBuilder setAlpha(final float alpha) {
152 myAlpha = alpha;
153 return this;
156 public PopupChooserBuilder setAutoselectOnMouseMove(final boolean doAutoSelect) {
157 myAutoselectOnMouseMove = doAutoSelect;
158 return this;
161 public PopupChooserBuilder setFilteringEnabled(Function<Object, String> namer) {
162 myItemsNamer = namer;
163 return this;
166 @NotNull
167 public JBPopup createPopup() {
168 JList list = null;
169 if (myChooserComponent instanceof JList) {
170 list = (JList)myChooserComponent;
171 myChooserComponent = ListWithFilter.wrap(list, new MyListWrapper(list), myItemsNamer);
174 JPanel contentPane = new JPanel(new BorderLayout());
175 if (!myForceMovable && myTitle != null) {
176 JLabel label = new JLabel(myTitle);
177 label.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5));
178 label.setHorizontalAlignment(SwingConstants.CENTER);
179 contentPane.add(label, BorderLayout.NORTH);
182 if (list != null) {
183 if (list.getSelectedIndex() == -1 && myAutoselect) {
184 list.setSelectedIndex(0);
188 myChooserComponent.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
190 (list != null ? list : myChooserComponent).addMouseListener(new MouseAdapter() {
191 @Override
192 public void mousePressed(MouseEvent e) {
193 if (UIUtil.isActionClick(e) && !isSelectionButtonDown(e) && !e.isConsumed()) {
194 closePopup(true, e);
199 registerClosePopupKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), false);
200 registerClosePopupKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), true);
201 for (KeyStroke keystroke : myAdditionalKeystrokes) {
202 registerClosePopupKeyboardAction(keystroke, true);
205 final JScrollPane scrollPane;
206 if (myChooserComponent instanceof ListWithFilter) {
207 scrollPane = ((ListWithFilter)myChooserComponent).getScrollPane();
209 else if (myChooserComponent instanceof JTable) {
210 scrollPane = createScrollPane((JTable)myChooserComponent);
212 else if (myChooserComponent instanceof JTree) {
213 scrollPane = createScrollPane((JTree)myChooserComponent);
215 else {
216 throw new IllegalStateException("PopupChooserBuilder is intended to be constructed with one of JTable, JTree, JList components");
219 scrollPane.getViewport().setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
221 if (myChooserComponent instanceof ListWithFilter) {
222 contentPane.add(myChooserComponent, BorderLayout.CENTER);
224 else {
225 contentPane.add(scrollPane, BorderLayout.CENTER);
228 if (mySouthComponent != null) {
229 contentPane.add(mySouthComponent, BorderLayout.SOUTH);
232 if (myEastComponent != null) {
233 contentPane.add(myEastComponent, BorderLayout.EAST);
236 ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(contentPane, myChooserComponent);
237 for (JBPopupListener each : myListeners) {
238 builder.addListener(each);
241 builder.setDimensionServiceKey(null, myDimensionServiceKey, false).setRequestFocus(myRequestFocus).setResizable(myForceResizable)
242 .setMovable(myForceMovable).setTitle(myForceMovable ? myTitle : null).setCancelCallback(myCancelCallback).setAlpha(myAlpha)
243 .setFocusOwners(myFocusOwners).setCancelKeyEnabled(myCancelKeyEnabled && !(myChooserComponent instanceof ListWithFilter)).
244 setAdText(myAd).setKeyboardActions(myKeyboardActions);
246 if (myCommandButton != null) {
247 builder.setCommandButton(myCommandButton);
250 if (myMinSize != null) {
251 builder.setMinSize(myMinSize);
253 if (mySettingsButtons != null) {
254 builder.setSettingButtons(mySettingsButtons);
256 myPopup = builder.createPopup();
257 return myPopup;
260 public PopupChooserBuilder setMinSize(final Dimension dimension) {
261 myMinSize = dimension;
262 return this;
265 public PopupChooserBuilder registerKeyboardAction(KeyStroke keyStroke, ActionListener actionListener) {
266 myKeyboardActions.add(Pair.create(actionListener, keyStroke));
267 return this;
270 private void registerClosePopupKeyboardAction(final KeyStroke keyStroke, final boolean shouldPerformAction) {
271 myChooserComponent.registerKeyboardAction(new AbstractAction() {
272 public void actionPerformed(ActionEvent e) {
273 if (!shouldPerformAction && myChooserComponent instanceof ListWithFilter) {
274 if (((ListWithFilter)myChooserComponent).resetFilter()) return;
276 closePopup(shouldPerformAction, null);
278 }, keyStroke, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
281 private void closePopup(boolean shouldPerformAction, MouseEvent e) {
282 if (shouldPerformAction) {
283 myPopup.setFinalRunnable(myItemChoosenRunnable);
286 myPopup.cancel(e);
289 @NotNull
290 private JScrollPane createScrollPane(final JTable table) {
291 if (table instanceof TreeTable) {
292 TreeUtil.expandAll(((TreeTable)table).getTree());
295 JScrollPane scrollPane = new JScrollPane(table);
297 scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
299 if (table.getSelectedRow() == -1) {
300 table.getSelectionModel().setSelectionInterval(0, 0);
303 if (table.getRowCount() >= 20) {
304 scrollPane.getViewport().setPreferredSize(new Dimension(table.getPreferredScrollableViewportSize().width, 300));
306 else {
307 scrollPane.getViewport().setPreferredSize(table.getPreferredSize());
310 if (myAutoselectOnMouseMove) {
311 table.addMouseMotionListener(new MouseMotionAdapter() {
312 boolean myIsEngaged = false;
313 public void mouseMoved(MouseEvent e) {
314 if (myIsEngaged) {
315 int index = table.rowAtPoint(e.getPoint());
316 table.getSelectionModel().setSelectionInterval(index, index);
318 else {
319 myIsEngaged = true;
325 return scrollPane;
328 @NotNull
329 private JScrollPane createScrollPane(final JTree tree) {
330 TreeUtil.expandAll(tree);
332 JScrollPane scrollPane = new JScrollPane(tree);
334 scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
336 if (tree.getSelectionCount() == 0) {
337 tree.setSelectionRow(0);
340 if (tree.getRowCount() >= 20) {
341 scrollPane.getViewport().setPreferredSize(new Dimension(tree.getPreferredScrollableViewportSize().width, 300));
343 else {
344 scrollPane.getViewport().setPreferredSize(tree.getPreferredSize());
347 if (myAutoselectOnMouseMove) {
348 tree.addMouseMotionListener(new MouseMotionAdapter() {
349 boolean myIsEngaged = false;
350 public void mouseMoved(MouseEvent e) {
351 if (myIsEngaged) {
352 final Point p = e.getPoint();
353 int index = tree.getRowForLocation(p.x, p.y);
354 tree.setSelectionRow(index);
356 else {
357 myIsEngaged = true;
363 return scrollPane;
366 public PopupChooserBuilder setAutoSelectIfEmpty(final boolean autoselect) {
367 myAutoselect = autoselect;
368 return this;
371 public PopupChooserBuilder setCancelKeyEnabled(final boolean enabled) {
372 myCancelKeyEnabled = enabled;
373 return this;
376 public PopupChooserBuilder addListener(final JBPopupListener listener) {
377 myListeners.add(listener);
378 return this;
381 private static boolean isSelectionButtonDown(MouseEvent e) {
382 return e.isShiftDown() || e.isControlDown() || e.isMetaDown();
385 public PopupChooserBuilder setSettingButton(Component abutton) {
386 mySettingsButtons = abutton;
387 return this;
390 private class MyListWrapper extends JScrollPane implements DataProvider {
391 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"})
392 private final JList myList;
394 private MyListWrapper(final JList list) {
395 super(list);
396 if (myAutoselectOnMouseMove) {
397 list.addMouseMotionListener(new MouseMotionAdapter() {
398 boolean myIsEngaged = false;
399 public void mouseMoved(MouseEvent e) {
400 if (myIsEngaged && !isSelectionButtonDown(e)) {
401 Point point = e.getPoint();
402 int index = list.locationToIndex(point);
403 list.setSelectedIndex(index);
405 else {
406 myIsEngaged = true;
412 ListScrollingUtil.installActions(list);
414 int modelSize = list.getModel().getSize();
415 setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
416 if (modelSize > 0 && modelSize <= 20) {
417 list.setVisibleRowCount(0);
418 getViewport().setPreferredSize(list.getPreferredSize());
420 else {
421 list.setVisibleRowCount(20);
423 myList = list;
427 @Nullable
428 public Object getData(@NonNls String dataId) {
429 if (PlatformDataKeys.SELECTED_ITEM.is(dataId)){
430 return myList.getSelectedValue();
432 return null;
435 public void setBorder(Border border) {
436 if (myList != null){
437 myList.setBorder(border);
441 public void requestFocus() {
442 myList.requestFocus();
445 public synchronized void addMouseListener(MouseListener l) {
446 myList.addMouseListener(l);
450 @NotNull
451 public PopupChooserBuilder setFocusOwners(@NotNull Component[] focusOwners) {
452 myFocusOwners = focusOwners;
453 return this;
456 @NotNull
457 public PopupChooserBuilder setAdText(String ad) {
458 myAd = ad;
459 return this;