- shadow for non-movable and non-resizeable popups
[fedora-idea.git] / platform-impl / src / com / intellij / ui / popup / WizardPopup.java
blob0cb0153bbc5f7285982d27fa36a18752210e373f
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;
7 import com.intellij.ide.DataManager;
8 import com.intellij.openapi.actionSystem.DataProvider;
9 import com.intellij.openapi.actionSystem.PlatformDataKeys;
10 import com.intellij.openapi.project.Project;
11 import com.intellij.openapi.ui.popup.*;
12 import com.intellij.openapi.util.Disposer;
13 import com.intellij.openapi.util.Pair;
14 import com.intellij.ui.PopupBorder;
15 import com.intellij.ui.ScreenUtil;
16 import com.intellij.ui.popup.list.ListPopupImpl;
17 import com.intellij.ui.popup.tree.TreePopupImpl;
18 import com.intellij.ui.popup.util.MnemonicsSearch;
19 import com.intellij.ui.speedSearch.ElementFilter;
20 import com.intellij.ui.speedSearch.SpeedSearch;
21 import org.jetbrains.annotations.NonNls;
22 import org.jetbrains.annotations.NotNull;
24 import javax.swing.*;
25 import java.awt.*;
26 import java.awt.event.*;
27 import java.util.Collections;
29 public abstract class WizardPopup extends AbstractPopup implements ActionListener, ElementFilter {
31 private static final int AUTO_POPUP_DELAY = 750;
32 private static final Dimension MAX_SIZE = new Dimension(Integer.MAX_VALUE, 600);
34 protected static final int STEP_X_PADDING = 2;
36 private final WizardPopup myParent;
38 protected final PopupStep<Object> myStep;
39 protected WizardPopup myChild;
41 private JScrollPane myScrollPane;
43 private final Timer myAutoSelectionTimer = new Timer(AUTO_POPUP_DELAY, this);
46 private MnemonicsSearch myMnemonicsSearch;
47 private Object myParentValue;
49 private Point myLastOwnerPoint;
50 private Window myOwnerWindow;
51 private MyComponentAdapter myOwnerListener;
53 public WizardPopup(PopupStep aStep) {
54 this(null, aStep);
57 public WizardPopup(JBPopup aParent, PopupStep aStep) {
58 myParent = (WizardPopup) aParent;
59 myStep = aStep;
61 mySpeedSearch.setEnabled(myStep.isSpeedSearchEnabled());
63 final JComponent content = createContent();
65 myScrollPane = new JScrollPane(content);
66 myScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
67 myScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
68 myScrollPane.getHorizontalScrollBar().setBorder(null);
70 myScrollPane.getActionMap().get("unitScrollLeft").setEnabled(false);
71 myScrollPane.getActionMap().get("unitScrollRight").setEnabled(false);
73 myScrollPane.setBorder(null);
75 final Project project = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext());
76 init(project, myScrollPane, getPreferredFocusableComponent(), true, true, true, true, null,
77 false, aStep.getTitle(), null, true, null, false, null, null, null, false, null, true, false, true, null, 0f,
78 null, true, false, new Component[0], null, true, Collections.<Pair<ActionListener, KeyStroke>>emptyList(), null);
80 registerAction("disposeAll", KeyEvent.VK_ESCAPE, InputEvent.SHIFT_MASK, new AbstractAction() {
81 public void actionPerformed(ActionEvent e) {
82 if (mySpeedSearch.isHoldingFilter()) {
83 mySpeedSearch.reset();
85 else {
86 disposeAll();
89 });
91 AbstractAction goBackAction = new AbstractAction() {
92 public void actionPerformed(ActionEvent e) {
93 goBack();
97 registerAction("goBack3", KeyEvent.VK_ESCAPE, 0, goBackAction);
99 myMnemonicsSearch = new MnemonicsSearch(this) {
100 protected void select(Object value) {
101 onSelectByMnemonic(value);
109 private void disposeAll() {
110 WizardPopup root = PopupDispatcher.getActiveRoot();
111 disposeAllParents(null);
112 root.getStep().canceled();
115 public void goBack() {
116 if (mySpeedSearch.isHoldingFilter()) {
117 mySpeedSearch.reset();
118 return;
121 if (myParent != null) {
122 myParent.disposeChildren();
124 else {
125 disposeAll();
129 protected abstract JComponent createContent();
131 public void dispose() {
132 super.dispose();
134 myAutoSelectionTimer.stop();
136 PopupDispatcher.unsetShowing(this);
137 PopupDispatcher.clearRootIfNeeded(this);
140 if (myOwnerWindow != null && myOwnerListener != null) {
141 myOwnerWindow.removeComponentListener(myOwnerListener);
146 public void disposeChildren() {
147 if (myChild != null) {
148 myChild.disposeChildren();
149 Disposer.dispose(myChild);
150 myChild = null;
154 public void show(final Component owner, final int aScreenX, final int aScreenY, final boolean considerForcedXY) {
155 Rectangle targetBounds = new Rectangle(new Point(aScreenX, aScreenY), getContent().getPreferredSize());
156 ScreenUtil.moveRectangleToFitTheScreen(targetBounds);
158 if (getParent() != null) {
159 if (getParent().getBounds().intersects(targetBounds)) {
160 targetBounds.x = getParent().getBounds().x - targetBounds.width - STEP_X_PADDING;
164 if (getParent() == null) {
165 PopupDispatcher.setActiveRoot(this);
167 else {
168 PopupDispatcher.setShowing(this);
171 super.show(owner, targetBounds.x, targetBounds.y, true);
174 protected void afterShow() {
175 super.afterShow();
176 registerAutoMove();
178 if (!myFocusTrackback.isMustBeShown()) {
179 cancel();
183 private void registerAutoMove() {
184 if (myOwner != null) {
185 myOwnerWindow = SwingUtilities.getWindowAncestor(myOwner);
186 if (myOwnerWindow != null) {
187 myLastOwnerPoint = myOwnerWindow.getLocationOnScreen();
188 myOwnerListener = new MyComponentAdapter();
189 myOwnerWindow.addComponentListener(myOwnerListener);
194 private void processParentWindowMoved() {
195 if (isDisposed()) return;
197 final Point newOwnerPoint = myOwnerWindow.getLocationOnScreen();
199 int deltaX = myLastOwnerPoint.x - newOwnerPoint.x;
200 int deltaY = myLastOwnerPoint.y - newOwnerPoint.y;
202 myLastOwnerPoint = newOwnerPoint;
204 final Window wnd = SwingUtilities.getWindowAncestor(getContent());
205 final Point current = wnd.getLocationOnScreen();
207 setLocation(new Point(current.x - deltaX, current.y - deltaY));
210 protected abstract JComponent getPreferredFocusableComponent();
212 public void cancel(InputEvent e) {
213 super.cancel(e);
214 disposeChildren();
215 Disposer.dispose(this);
216 getStep().canceled();
219 @Override
220 public boolean isCancelKeyEnabled() {
221 return super.isCancelKeyEnabled() && !mySpeedSearch.isHoldingFilter();
224 protected void disposeAllParents(InputEvent e) {
225 myDisposeEvent = e;
226 dispose();
227 if (myParent != null) {
228 myParent.disposeAllParents(null);
232 public final void registerAction(@NonNls String aActionName, int aKeyCode, int aModifier, Action aAction) {
233 getInputMap().put(KeyStroke.getKeyStroke(aKeyCode, aModifier), aActionName);
234 getActionMap().put(aActionName, aAction);
237 protected abstract InputMap getInputMap();
239 protected abstract ActionMap getActionMap();
241 protected final void setParentValue(Object parentValue) {
242 myParentValue = parentValue;
245 @NotNull
246 protected MyContentPanel createContentPanel(final boolean resizable, final PopupBorder border, final boolean isToDrawMacCorner) {
247 return new MyContainer(resizable, border, isToDrawMacCorner);
250 private static class MyContainer extends MyContentPanel implements DataProvider {
252 private MyContainer(final boolean resizable, final PopupBorder border, final boolean drawMacCorner) {
253 super(resizable, border, drawMacCorner);
254 setOpaque(true);
255 setFocusCycleRoot(true);
258 public Object getData(String dataId) {
259 return null;
262 public Dimension getPreferredSize() {
263 return computeNotBiggerDimension(super.getPreferredSize());
266 private static Dimension computeNotBiggerDimension(Dimension ofContent) {
267 int resultWidth = ofContent.width > MAX_SIZE.width ? MAX_SIZE.width : ofContent.width;
268 int resultHeight = ofContent.height > MAX_SIZE.height ? MAX_SIZE.height : ofContent.height;
270 return new Dimension(resultWidth, resultHeight);
274 public WizardPopup getParent() {
275 return myParent;
278 public PopupStep getStep() {
279 return myStep;
282 public final void dispatch(KeyEvent event) {
283 if (event.getID() != KeyEvent.KEY_PRESSED && event.getID() != KeyEvent.KEY_RELEASED) {
284 return;
287 if (event.getID() == KeyEvent.KEY_PRESSED) {
288 final KeyStroke stroke = KeyStroke.getKeyStroke(event.getKeyChar(), event.getModifiers(), false);
289 if (proceedKeyEvent(event, stroke)) return;
292 if (event.getID() == KeyEvent.KEY_RELEASED) {
293 final KeyStroke stroke = KeyStroke.getKeyStroke(event.getKeyCode(), event.getModifiers(), true);
294 proceedKeyEvent(event, stroke);
295 return;
298 myMnemonicsSearch.process(event);
299 mySpeedSearch.process(event);
301 if (event.isConsumed()) return;
302 process(event);
305 private boolean proceedKeyEvent(KeyEvent event, KeyStroke stroke) {
306 if (getInputMap().get(stroke) != null) {
307 final Action action = getActionMap().get(getInputMap().get(stroke));
308 if (action != null && action.isEnabled()) {
309 action.actionPerformed(new ActionEvent(getContent(), event.getID(), "", event.getWhen(), event.getModifiers()));
310 return true;
313 return false;
316 protected void process(KeyEvent aEvent) {
320 public Rectangle getBounds() {
321 return new Rectangle(getContent().getLocationOnScreen(), getContent().getSize());
324 protected static WizardPopup createPopup(WizardPopup parent, PopupStep step, Object parentValue) {
325 if (step instanceof ListPopupStep) {
326 return new ListPopupImpl(parent, (ListPopupStep)step, parentValue);
328 else if (step instanceof TreePopupStep) {
329 return new TreePopupImpl(parent, (TreePopupStep)step, parentValue);
331 else {
332 throw new IllegalArgumentException(step.getClass().toString());
336 public final void actionPerformed(ActionEvent e) {
337 myAutoSelectionTimer.stop();
338 if (getStep().isAutoSelectionEnabled()) {
339 onAutoSelectionTimer();
343 protected final void restartTimer() {
344 if (!myAutoSelectionTimer.isRunning()) {
345 myAutoSelectionTimer.start();
347 else {
348 myAutoSelectionTimer.restart();
352 protected final void stopTimer() {
353 myAutoSelectionTimer.stop();
356 protected void onAutoSelectionTimer() {
360 public boolean shouldBeShowing(Object value) {
361 if (!myStep.isSpeedSearchEnabled()) return true;
362 SpeedSearchFilter<Object> filter = myStep.getSpeedSearchFilter();
363 if (!filter.canBeHidden(value)) return true;
364 String text = filter.getIndexedString(value);
365 return mySpeedSearch.shouldBeShowing(text);
368 public SpeedSearch getSpeedSearch() {
369 return mySpeedSearch;
373 protected void onSelectByMnemonic(Object value) {
377 protected abstract void onChildSelectedFor(Object value);
379 protected final void notifyParentOnChildSelection() {
380 if (myParent == null || myParentValue == null) return;
381 myParent.onChildSelectedFor(myParentValue);
385 private class MyComponentAdapter extends ComponentAdapter {
386 public void componentMoved(final ComponentEvent e) {
387 processParentWindowMoved();