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
;
36 import javax
.swing
.border
.Border
;
38 import java
.awt
.event
.*;
39 import java
.util
.ArrayList
;
40 import java
.util
.List
;
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
>();
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
;
88 public PopupChooserBuilder
setTitle(@NotNull @Nls String title
) {
94 public PopupChooserBuilder
addAdditionalChooseKeystroke(@Nullable KeyStroke keyStroke
) {
95 if (keyStroke
!= null) {
96 myAdditionalKeystrokes
.add(keyStroke
);
102 public PopupChooserBuilder
setItemChoosenCallback(@NotNull Runnable runnable
) {
103 myItemChoosenRunnable
= runnable
;
108 public PopupChooserBuilder
setSouthComponent(@NotNull JComponent cmp
) {
109 mySouthComponent
= cmp
;
114 public PopupChooserBuilder
setEastComponent(@NotNull JComponent cmp
) {
115 myEastComponent
= cmp
;
120 public PopupChooserBuilder
setRequestFocus(final boolean requestFocus
) {
121 myRequestFocus
= requestFocus
;
125 public PopupChooserBuilder
setResizable(final boolean forceResizable
) {
126 myForceResizable
= forceResizable
;
131 public PopupChooserBuilder
setMovable(final boolean forceMovable
) {
132 myForceMovable
= forceMovable
;
136 public PopupChooserBuilder
setDimensionServiceKey(@NonNls String key
){
137 myDimensionServiceKey
= key
;
141 public PopupChooserBuilder
setCancelCallback(Computable
<Boolean
> callback
) {
142 myCancelCallback
= callback
;
146 public PopupChooserBuilder
setCommandButton(@NotNull InplaceButton commandButton
) {
147 myCommandButton
= commandButton
;
151 public PopupChooserBuilder
setAlpha(final float alpha
) {
156 public PopupChooserBuilder
setAutoselectOnMouseMove(final boolean doAutoSelect
) {
157 myAutoselectOnMouseMove
= doAutoSelect
;
161 public PopupChooserBuilder
setFilteringEnabled(Function
<Object
, String
> namer
) {
162 myItemsNamer
= namer
;
167 public JBPopup
createPopup() {
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
);
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() {
192 public void mousePressed(MouseEvent e
) {
193 if (UIUtil
.isActionClick(e
) && !isSelectionButtonDown(e
) && !e
.isConsumed()) {
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
);
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
);
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();
260 public PopupChooserBuilder
setMinSize(final Dimension dimension
) {
261 myMinSize
= dimension
;
265 public PopupChooserBuilder
registerKeyboardAction(KeyStroke keyStroke
, ActionListener actionListener
) {
266 myKeyboardActions
.add(Pair
.create(actionListener
, keyStroke
));
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
);
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));
307 scrollPane
.getViewport().setPreferredSize(table
.getPreferredSize());
310 if (myAutoselectOnMouseMove
) {
311 table
.addMouseMotionListener(new MouseMotionAdapter() {
312 boolean myIsEngaged
= false;
313 public void mouseMoved(MouseEvent e
) {
315 int index
= table
.rowAtPoint(e
.getPoint());
316 table
.getSelectionModel().setSelectionInterval(index
, index
);
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));
344 scrollPane
.getViewport().setPreferredSize(tree
.getPreferredSize());
347 if (myAutoselectOnMouseMove
) {
348 tree
.addMouseMotionListener(new MouseMotionAdapter() {
349 boolean myIsEngaged
= false;
350 public void mouseMoved(MouseEvent e
) {
352 final Point p
= e
.getPoint();
353 int index
= tree
.getRowForLocation(p
.x
, p
.y
);
354 tree
.setSelectionRow(index
);
366 public PopupChooserBuilder
setAutoSelectIfEmpty(final boolean autoselect
) {
367 myAutoselect
= autoselect
;
371 public PopupChooserBuilder
setCancelKeyEnabled(final boolean enabled
) {
372 myCancelKeyEnabled
= enabled
;
376 public PopupChooserBuilder
addListener(final JBPopupListener listener
) {
377 myListeners
.add(listener
);
381 private static boolean isSelectionButtonDown(MouseEvent e
) {
382 return e
.isShiftDown() || e
.isControlDown() || e
.isMetaDown();
385 public PopupChooserBuilder
setSettingButton(Component abutton
) {
386 mySettingsButtons
= abutton
;
390 private class MyListWrapper
extends JScrollPane
implements DataProvider
{
391 @SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"})
392 private final JList myList
;
394 private MyListWrapper(final JList 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
);
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());
421 list
.setVisibleRowCount(20);
428 public Object
getData(@NonNls String dataId
) {
429 if (PlatformDataKeys
.SELECTED_ITEM
.is(dataId
)){
430 return myList
.getSelectedValue();
435 public void setBorder(Border border
) {
437 myList
.setBorder(border
);
441 public void requestFocus() {
442 myList
.requestFocus();
445 public synchronized void addMouseListener(MouseListener l
) {
446 myList
.addMouseListener(l
);
451 public PopupChooserBuilder
setFocusOwners(@NotNull Component
[] focusOwners
) {
452 myFocusOwners
= focusOwners
;
457 public PopupChooserBuilder
setAdText(String ad
) {