replaced <code></code> with more concise {@code}
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / ui / impl / DialogWrapperPeerImpl.java
1 /*
2  * Copyright 2000-2017 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.openapi.ui.impl;
17
18 import com.intellij.ide.DataManager;
19 import com.intellij.ide.IdeEventQueue;
20 import com.intellij.ide.impl.TypeSafeDataProviderAdapter;
21 import com.intellij.ide.ui.AntialiasingType;
22 import com.intellij.ide.ui.UISettings;
23 import com.intellij.openapi.actionSystem.*;
24 import com.intellij.openapi.application.Application;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.application.ex.ApplicationEx;
27 import com.intellij.openapi.application.ex.ApplicationManagerEx;
28 import com.intellij.openapi.application.impl.LaterInvocator;
29 import com.intellij.openapi.command.CommandProcessor;
30 import com.intellij.openapi.command.CommandProcessorEx;
31 import com.intellij.openapi.diagnostic.Logger;
32 import com.intellij.openapi.project.DumbAware;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.openapi.ui.DialogWrapper;
35 import com.intellij.openapi.ui.DialogWrapperDialog;
36 import com.intellij.openapi.ui.DialogWrapperPeer;
37 import com.intellij.openapi.ui.Queryable;
38 import com.intellij.openapi.ui.popup.StackingPopupDispatcher;
39 import com.intellij.openapi.util.*;
40 import com.intellij.openapi.util.registry.Registry;
41 import com.intellij.openapi.wm.IdeFocusManager;
42 import com.intellij.openapi.wm.IdeFrame;
43 import com.intellij.openapi.wm.WindowManager;
44 import com.intellij.openapi.wm.ex.LayoutFocusTraversalPolicyExt;
45 import com.intellij.openapi.wm.ex.WindowManagerEx;
46 import com.intellij.openapi.wm.impl.IdeFrameImpl;
47 import com.intellij.openapi.wm.impl.IdeGlassPaneImpl;
48 import com.intellij.reference.SoftReference;
49 import com.intellij.ui.*;
50 import com.intellij.ui.components.JBLayeredPane;
51 import com.intellij.ui.mac.foundation.Foundation;
52 import com.intellij.ui.mac.foundation.ID;
53 import com.intellij.ui.mac.foundation.MacUtil;
54 import com.intellij.util.IJSwingUtilities;
55 import com.intellij.util.ReflectionUtil;
56 import com.intellij.util.ui.GraphicsUtil;
57 import com.intellij.util.ui.JBInsets;
58 import com.intellij.util.ui.OwnerOptional;
59 import com.intellij.util.ui.UIUtil;
60 import org.jetbrains.annotations.NonNls;
61 import org.jetbrains.annotations.NotNull;
62 import org.jetbrains.annotations.Nullable;
63
64 import javax.swing.*;
65 import java.awt.*;
66 import java.awt.event.*;
67 import java.awt.image.BufferStrategy;
68 import java.lang.ref.WeakReference;
69 import java.util.ArrayList;
70 import java.util.List;
71 import java.util.Map;
72
73 public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTrackbackProvider {
74   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.ui.DialogWrapper");
75
76   private final DialogWrapper myWrapper;
77   private AbstractDialog myDialog;
78   private boolean myCanBeParent = true;
79   private WindowManagerEx myWindowManager;
80   private final List<Runnable> myDisposeActions = new ArrayList<>();
81   private Project myProject;
82
83   private final ActionCallback myWindowFocusedCallback = new ActionCallback("DialogFocusedCallback");
84   private final ActionCallback myTypeAheadDone = new ActionCallback("DialogTypeAheadDone");
85   private ActionCallback myTypeAheadCallback;
86
87   protected DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, @Nullable Project project, boolean canBeParent, @NotNull DialogWrapper.IdeModalityType ideModalityType) {
88     myWrapper = wrapper;
89     myTypeAheadCallback = myWrapper.isTypeAheadEnabled() ? new ActionCallback() : null;
90     myWindowManager = null;
91     Application application = ApplicationManager.getApplication();
92     if (application != null && application.hasComponent(WindowManager.class)) {
93       myWindowManager = (WindowManagerEx)WindowManager.getInstance();
94     }
95
96     Window window = null;
97     if (myWindowManager != null) {
98
99       if (project == null) {
100         //noinspection deprecation
101         project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext());
102       }
103
104       myProject = project;
105
106       window = myWindowManager.suggestParentWindow(project);
107       if (window == null) {
108         Window focusedWindow = myWindowManager.getMostRecentFocusedWindow();
109         if (focusedWindow instanceof IdeFrameImpl) {
110           window = focusedWindow;
111         }
112       }
113       if (window == null) {
114         IdeFrame[] frames = myWindowManager.getAllProjectFrames();
115         for (IdeFrame frame : frames) {
116           if (frame instanceof IdeFrameImpl && ((IdeFrameImpl)frame).isActive()) {
117             window = (IdeFrameImpl)frame;
118             break;
119           }
120         }
121       }
122     }
123
124     Window owner;
125     if (window != null) {
126       owner = window;
127     }
128     else {
129       if (!isHeadless()) {
130         owner = JOptionPane.getRootFrame();
131       } else {
132         owner = null;
133       }
134     }
135
136     createDialog(owner, canBeParent, ideModalityType);
137   }
138
139   /**
140    * Creates modal {@code DialogWrapper}. The currently active window will be the dialog's parent.
141    *
142    * @param project     parent window for the dialog will be calculated based on focused window for the
143    *                    specified {@code project}. This parameter can be {@code null}. In this case parent window
144    *                    will be suggested based on current focused window.
145    * @param canBeParent specifies whether the dialog can be parent for other windows. This parameter is used
146    *                    by {@code WindowManager}.
147    */
148   protected DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, @Nullable Project project, boolean canBeParent) {
149     this(wrapper, project, canBeParent, DialogWrapper.IdeModalityType.IDE);
150   }
151
152   protected DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, boolean canBeParent) {
153     this(wrapper, (Project)null, canBeParent);
154   }
155
156   @Override
157   public boolean isHeadless() {
158     return isHeadlessEnv();
159   }
160
161   @Override
162   public Object[] getCurrentModalEntities() {
163     return LaterInvocator.getCurrentModalEntities();
164   }
165
166   public static boolean isHeadlessEnv() {
167     Application app = ApplicationManager.getApplication();
168     if (app == null) return GraphicsEnvironment.isHeadless();
169
170     return app.isUnitTestMode() || app.isHeadlessEnvironment();
171   }
172
173   /**
174    * @param parent parent component which is used to calculate heavy weight window ancestor.
175    *               {@code parent} cannot be {@code null} and must be showing.
176    */
177   protected DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, @NotNull Component parent, final boolean canBeParent) {
178     myWrapper = wrapper;
179
180     myWindowManager = null;
181     Application application = ApplicationManager.getApplication();
182     if (application != null && application.hasComponent(WindowManager.class)) {
183       myWindowManager = (WindowManagerEx)WindowManager.getInstance();
184     }
185
186     OwnerOptional.fromComponent(parent).ifWindow(window -> {
187       createDialog(window, canBeParent);
188     });
189   }
190
191   public DialogWrapperPeerImpl(@NotNull final DialogWrapper wrapper,final Window owner, final boolean canBeParent,
192                                final DialogWrapper.IdeModalityType ideModalityType ) {
193     myWrapper = wrapper;
194     myWindowManager = null;
195     Application application = ApplicationManager.getApplication();
196     if (application != null && application.hasComponent(WindowManager.class)) {
197       myWindowManager = (WindowManagerEx)WindowManager.getInstance();
198     }
199     createDialog(owner, canBeParent);
200
201     if (!isHeadless()) {
202       Dialog.ModalityType modalityType = DialogWrapper.IdeModalityType.IDE.toAwtModality();
203       if (Registry.is("ide.perProjectModality")) {
204         modalityType = ideModalityType.toAwtModality();
205       }
206       myDialog.setModalityType(modalityType);
207     }
208   }
209
210   /** @see DialogWrapper#DialogWrapper(boolean, boolean)
211    */
212   @Deprecated
213   public DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper, final boolean canBeParent, final boolean applicationModalIfPossible) {
214     this(wrapper, null, canBeParent, applicationModalIfPossible);
215   }
216
217   @Deprecated
218   public DialogWrapperPeerImpl(@NotNull DialogWrapper wrapper,final Window owner, final boolean canBeParent, final boolean applicationModalIfPossible) {
219     this(wrapper, owner, canBeParent, applicationModalIfPossible ? DialogWrapper.IdeModalityType.IDE : DialogWrapper.IdeModalityType.PROJECT);
220   }
221
222   @Override
223   public void setUndecorated(boolean undecorated) {
224     myDialog.setUndecorated(undecorated);
225   }
226
227   @Override
228   public void addMouseListener(MouseListener listener) {
229     myDialog.addMouseListener(listener);
230   }
231
232   @Override
233   public void addMouseListener(MouseMotionListener listener) {
234     myDialog.addMouseMotionListener(listener);
235   }
236
237   @Override
238   public void addKeyListener(KeyListener listener) {
239     myDialog.addKeyListener(listener);
240   }
241
242   private void createDialog(@Nullable Window owner, boolean canBeParent, @NotNull DialogWrapper.IdeModalityType ideModalityType) {
243     if (isHeadless()) {
244       myDialog = new HeadlessDialog(myWrapper);
245     }
246     else {
247       myDialog = new MyDialog(owner, myWrapper, myProject, myWindowFocusedCallback, myTypeAheadDone, myTypeAheadCallback);
248
249       UIUtil.suppressFocusStealing(getWindow());
250
251       myDialog.setModalityType(ideModalityType.toAwtModality());
252
253       myCanBeParent = canBeParent;
254     }
255   }
256
257   private void createDialog(@Nullable Window owner, boolean canBeParent) {
258     createDialog(owner, canBeParent, DialogWrapper.IdeModalityType.IDE);
259   }
260
261   @Override
262   public void toFront() {
263     myDialog.toFront();
264   }
265
266   @Override
267   public void toBack() {
268     myDialog.toBack();
269   }
270
271   @Override
272   @SuppressWarnings("SSBasedInspection")
273   protected void dispose() {
274     LOG.assertTrue(EventQueue.isDispatchThread(), "Access is allowed from event dispatch thread only");
275     for (Runnable runnable : myDisposeActions) {
276       runnable.run();
277     }
278     myDisposeActions.clear();
279     Runnable disposer = () -> {
280       Disposer.dispose(myDialog);
281       myProject = null;
282
283       SwingUtilities.invokeLater(() -> {
284         if (myDialog != null && myDialog.getRootPane() != null) {
285           myDialog.remove(myDialog.getRootPane());
286         }
287       });
288     };
289
290     UIUtil.invokeLaterIfNeeded(disposer);
291   }
292
293   private boolean isProgressDialog() {
294     return myWrapper.isModalProgress();
295   }
296
297   @Override
298   @Nullable
299   public Container getContentPane() {
300     return getRootPane() != null ? myDialog.getContentPane() : null;
301   }
302
303   /**
304    * @see javax.swing.JDialog#validate
305    */
306   @Override
307   public void validate() {
308     myDialog.validate();
309   }
310
311   /**
312    * @see javax.swing.JDialog#repaint
313    */
314   @Override
315   public void repaint() {
316     myDialog.repaint();
317   }
318
319   @Override
320   public Window getOwner() {
321     return myDialog.getOwner();
322   }
323
324   @Override
325   public Window getWindow() {
326     return myDialog.getWindow();
327   }
328
329   @Override
330   public JRootPane getRootPane() {
331     return myDialog.getRootPane();
332   }
333
334   @Override
335   public Dimension getSize() {
336     return myDialog.getSize();
337   }
338
339   @Override
340   public String getTitle() {
341     return myDialog.getTitle();
342   }
343
344   /**
345    * @see java.awt.Window#pack
346    */
347   @Override
348   public void pack() {
349     myDialog.pack();
350   }
351
352   @Override
353   public void setAppIcons() {
354     AppUIUtil.updateWindowIcon(getWindow());
355   }
356
357   @Override
358   public Dimension getPreferredSize() {
359     return myDialog.getPreferredSize();
360   }
361
362   @Override
363   public void setModal(boolean modal) {
364     myDialog.setModal(modal);
365   }
366
367   @Override
368   public boolean isModal() {
369     return myDialog.isModal();
370   }
371
372   @Override
373   public boolean isVisible() {
374     return myDialog.isVisible();
375   }
376
377   @Override
378   public boolean isShowing() {
379     return myDialog.isShowing();
380   }
381
382   @Override
383   public void setSize(int width, int height) {
384     myDialog.setSize(width, height);
385   }
386
387   @Override
388   public void setTitle(String title) {
389     myDialog.setTitle(title);
390   }
391
392   @Override
393   public void isResizable() {
394     myDialog.isResizable();
395   }
396
397   @Override
398   public void setResizable(boolean resizable) {
399     myDialog.setResizable(resizable);
400   }
401
402   @NotNull
403   @Override
404   public Point getLocation() {
405     return myDialog.getLocation();
406   }
407
408   @Override
409   public void setLocation(@NotNull Point p) {
410     myDialog.setLocation(p);
411   }
412
413   @Override
414   public void setLocation(int x, int y) {
415     myDialog.setLocation(x, y);
416   }
417
418   @Override
419   public ActionCallback show() {
420     LOG.assertTrue(EventQueue.isDispatchThread(), "Access is allowed from event dispatch thread only");
421     if (myTypeAheadCallback != null) {
422       IdeFocusManager.getInstance(myProject).typeAheadUntil(myTypeAheadCallback);
423     }                         LOG.assertTrue(EventQueue.isDispatchThread(), "Access is allowed from event dispatch thread only");
424     final ActionCallback result = new ActionCallback();
425
426     final AnCancelAction anCancelAction = new AnCancelAction();
427     final JRootPane rootPane = getRootPane();
428     UIUtil.decorateFrame(rootPane);
429     anCancelAction.registerCustomShortcutSet(CommonShortcuts.ESCAPE, rootPane);
430     myDisposeActions.add(() -> anCancelAction.unregisterCustomShortcutSet(rootPane));
431
432     if (!myCanBeParent && myWindowManager != null) {
433       myWindowManager.doNotSuggestAsParent(myDialog.getWindow());
434     }
435
436     final CommandProcessorEx commandProcessor =
437       ApplicationManager.getApplication() != null ? (CommandProcessorEx)CommandProcessor.getInstance() : null;
438     final boolean appStarted = commandProcessor != null;
439
440     boolean changeModalityState = appStarted && myDialog.isModal()
441                                   && !isProgressDialog(); // ProgressWindow starts a modality state itself
442     Project project = myProject;
443
444     if (changeModalityState) {
445       commandProcessor.enterModal();
446       if (Registry.is("ide.perProjectModality")) {
447         LaterInvocator.enterModal(project, myDialog.getWindow());
448       } else {
449         LaterInvocator.enterModal(myDialog);
450       }
451     }
452
453     if (appStarted) {
454       hidePopupsIfNeeded();
455     }
456
457     try {
458       myDialog.show();
459     }
460     finally {
461       if (changeModalityState) {
462         commandProcessor.leaveModal();
463         if (Registry.is("ide.perProjectModality")) {
464           LaterInvocator.leaveModal(project, myDialog.getWindow());
465         } else {
466           LaterInvocator.leaveModal(myDialog);
467         }
468       }
469
470       myDialog.getFocusManager().doWhenFocusSettlesDown(result.createSetDoneRunnable());
471     }
472
473     return result;
474   }
475
476   //hopefully this whole code will go away
477   private void hidePopupsIfNeeded() {
478     if (!SystemInfo.isMac) return;
479
480     StackingPopupDispatcher.getInstance().hidePersistentPopups();
481     myDisposeActions.add(() -> StackingPopupDispatcher.getInstance().restorePersistentPopups());
482   }
483
484   @Override
485   public FocusTrackback getFocusTrackback() {
486     return myDialog.getFocusTrackback();
487   }
488
489   private class AnCancelAction extends AnAction implements DumbAware {
490
491     @Override
492     public void update(AnActionEvent e) {
493       Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
494       e.getPresentation().setEnabled(false);
495       if (focusOwner instanceof JComponent && SpeedSearchBase.hasActiveSpeedSearch((JComponent)focusOwner)) {
496         return;
497       }
498
499       if (StackingPopupDispatcher.getInstance().isPopupFocused()) return;
500       JTree tree = UIUtil.getParentOfType(JTree.class, focusOwner);
501       JTable table = UIUtil.getParentOfType(JTable.class, focusOwner);
502
503       if (tree != null || table != null) {
504         if (hasNoEditingTreesOrTablesUpward(focusOwner)) {
505           e.getPresentation().setEnabled(true);
506         }
507       }
508     }
509
510     private boolean hasNoEditingTreesOrTablesUpward(Component comp) {
511       while (comp != null) {
512         if (isEditingTreeOrTable(comp)) return false;
513         comp = comp.getParent();
514       }
515       return true;
516     }
517
518     private boolean isEditingTreeOrTable(Component comp) {
519       if (comp instanceof JTree) {
520         return ((JTree)comp).isEditing();
521       }
522       else if (comp instanceof JTable) {
523         return ((JTable)comp).isEditing();
524       }
525       return false;
526     }
527
528     @Override
529     public void actionPerformed(AnActionEvent e) {
530       myWrapper.doCancelAction(e.getInputEvent());
531     }
532   }
533
534
535   private static class MyDialog extends JDialog implements DialogWrapperDialog, DataProvider, FocusTrackback.Provider, Queryable, AbstractDialog {
536     private final WeakReference<DialogWrapper> myDialogWrapper;
537
538     /**
539      * Initial size of the dialog. When the dialog is being closed and
540      * current size of the dialog is not equals to the initial size then the
541      * current (changed) size is stored in the {@code DimensionService}.
542      */
543     private Dimension myInitialSize;
544     private String myDimensionServiceKey;
545     private boolean myOpened = false;
546     private boolean myActivated = false;
547
548     private FocusTrackback myFocusTrackback;
549     private MyDialog.MyWindowListener myWindowListener;
550
551     private final WeakReference<Project> myProject;
552     private final ActionCallback myFocusedCallback;
553     private final ActionCallback myTypeAheadDone;
554     private final ActionCallback myTypeAheadCallback;
555
556     public MyDialog(Window owner,
557                     DialogWrapper dialogWrapper,
558                     Project project,
559                     @NotNull ActionCallback focused,
560                     @NotNull ActionCallback typeAheadDone,
561                     ActionCallback typeAheadCallback) {
562       super(owner);
563       myDialogWrapper = new WeakReference<>(dialogWrapper);
564       myProject = project != null ? new WeakReference<>(project) : null;
565
566       setFocusTraversalPolicy(new LayoutFocusTraversalPolicyExt() {
567         @Override
568         protected boolean accept(Component aComponent) {
569           if (UIUtil.isFocusProxy(aComponent)) return false;
570           return super.accept(aComponent);
571         }
572       });
573
574       myFocusedCallback = focused;
575       myTypeAheadDone = typeAheadDone;
576       myTypeAheadCallback = typeAheadCallback;
577
578       final long typeAhead = getDialogWrapper().getTypeAheadTimeoutMs();
579       if (typeAhead <= 0) {
580         myTypeAheadDone.setDone();
581       }
582
583       setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
584       myWindowListener = new MyWindowListener();
585       addWindowListener(myWindowListener);
586     }
587
588     @Override
589     public JDialog getWindow() {
590       return this;
591     }
592
593     @Override
594     public void putInfo(@NotNull Map<String, String> info) {
595       info.put("dialog", getTitle());
596     }
597
598     @Override
599     public FocusTrackback getFocusTrackback() {
600       return myFocusTrackback;
601     }
602
603     @Override
604     public DialogWrapper getDialogWrapper() {
605       return myDialogWrapper.get();
606     }
607
608     @Override
609     public void centerInParent() {
610       setLocationRelativeTo(getOwner());
611     }
612
613     @Override
614     public Object getData(String dataId) {
615       final DialogWrapper wrapper = myDialogWrapper.get();
616       if (wrapper instanceof DataProvider) {
617         return ((DataProvider)wrapper).getData(dataId);
618       }
619       if (wrapper instanceof TypeSafeDataProvider) {
620         TypeSafeDataProviderAdapter adapter = new TypeSafeDataProviderAdapter((TypeSafeDataProvider)wrapper);
621         return adapter.getData(dataId);
622       }
623       return null;
624     }
625
626     @Override
627     public void setSize(int width, int height) {
628       _setSizeForLocation(width, height, null);
629     }
630
631     private void _setSizeForLocation(int width, int height, @Nullable Point initial) {
632       Point location = initial != null ? initial : getLocation();
633       Rectangle rect = new Rectangle(location.x, location.y, width, height);
634       ScreenUtil.fitToScreen(rect);
635       if (initial != null || location.x != rect.x || location.y != rect.y) {
636         setLocation(rect.x, rect.y);
637       }
638
639       super.setSize(rect.width, rect.height);
640     }
641
642     @Override
643     public void setBounds(int x, int y, int width, int height) {
644       Rectangle rect = new Rectangle(x, y, width, height);
645       ScreenUtil.fitToScreen(rect);
646       super.setBounds(rect.x, rect.y, rect.width, rect.height);
647     }
648
649     @Override
650     public void setBounds(Rectangle r) {
651       ScreenUtil.fitToScreen(r);
652       super.setBounds(r);
653     }
654
655     @NotNull
656     @Override
657     protected JRootPane createRootPane() {
658       return new DialogRootPane();
659     }
660
661     @Override
662     @SuppressWarnings("deprecation")
663     public void show() {
664       myFocusTrackback = new FocusTrackback(getDialogWrapper(), getParent(), true);
665
666       final DialogWrapper dialogWrapper = getDialogWrapper();
667       boolean isAutoAdjustable = dialogWrapper.isAutoAdjustable();
668       Point location = null;
669       if (isAutoAdjustable) {
670         pack();
671
672         Dimension packedSize = getSize();
673         Dimension minSize = getMinimumSize();
674         setSize(Math.max(packedSize.width, minSize.width), Math.max(packedSize.height, minSize.height));
675
676         setSize((int)(getWidth() * dialogWrapper.getHorizontalStretch()), (int)(getHeight() * dialogWrapper.getVerticalStretch()));
677
678         // Restore dialog's size and location
679
680         myDimensionServiceKey = dialogWrapper.getDimensionKey();
681
682         if (myDimensionServiceKey != null) {
683           final Project projectGuess = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(this));
684           location = DimensionService.getInstance().getLocation(myDimensionServiceKey, projectGuess);
685           Dimension size = DimensionService.getInstance().getSize(myDimensionServiceKey, projectGuess);
686           if (size != null) {
687             myInitialSize = new Dimension(size);
688             _setSizeForLocation(myInitialSize.width, myInitialSize.height, location);
689           }
690         }
691
692         if (myInitialSize == null) {
693           myInitialSize = getSize();
694         }
695       }
696
697       if (location == null) {
698         location = dialogWrapper.getInitialLocation();
699       }
700
701       if (location != null) {
702         setLocation(location);
703       }
704       else {
705         setLocationRelativeTo(getOwner());
706       }
707
708       if (isAutoAdjustable) {
709         final Rectangle bounds = getBounds();
710         ScreenUtil.fitToScreen(bounds);
711         setBounds(bounds);
712       }
713
714       if (Registry.is("actionSystem.fixLostTyping")) {
715         final IdeEventQueue queue = IdeEventQueue.getInstance();
716         if (queue != null) {
717           queue.getKeyEventDispatcher().resetState();
718         }
719
720       }
721
722       // Workaround for switching workspaces on dialog show
723       if (SystemInfo.isMac && myProject != null && Registry.is("ide.mac.fix.dialog.showing") && !dialogWrapper.isModalProgress()) {
724         final IdeFrame frame = WindowManager.getInstance().getIdeFrame(myProject.get());
725         AppIcon.getInstance().requestFocus(frame);
726       }
727
728       setBackground(UIUtil.getPanelBackground());
729
730       final ApplicationEx app = ApplicationManagerEx.getApplicationEx();
731       if (app != null && !app.isLoaded() && Splash.BOUNDS != null) {
732         final Point loc = getLocation();
733         loc.y = Splash.BOUNDS.y + Splash.BOUNDS.height;
734         setLocation(loc);
735       }
736       super.show();
737     }
738
739     @Nullable
740     private Project getProject() {
741       return SoftReference.dereference(myProject);
742     }
743
744     @NotNull
745     @Override
746     public IdeFocusManager getFocusManager() {
747       Project project = getProject();
748       if (project != null && !project.isDisposed()) {
749         return IdeFocusManager.getInstance(project);
750       }
751       else {
752         return IdeFocusManager.findInstance();
753       }
754     }
755
756     private void disposeFocusTrackbackIfNoChildWindowFocused(@Nullable IdeFocusManager focusManager) {
757       if (myFocusTrackback == null) return;
758
759       final DialogWrapper wrapper = myDialogWrapper.get();
760       if (wrapper == null || !wrapper.isShowing()) {
761         myFocusTrackback.dispose();
762         return;
763       }
764
765       if (focusManager != null) {
766         final Component c = focusManager.getFocusedDescendantFor(wrapper.getContentPane());
767         if (c == null) {
768           myFocusTrackback.dispose();
769         }
770       }
771       else {
772         final Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
773         if (owner == null || !SwingUtilities.isDescendingFrom(owner, wrapper.getContentPane())) {
774           myFocusTrackback.dispose();
775         }
776       }
777     }
778
779     @Override
780     @SuppressWarnings("deprecation")
781     public void hide() {
782       super.hide();
783       if (myFocusTrackback != null && !(myFocusTrackback.isScheduledForRestore() || myFocusTrackback.isWillBeScheduledForRestore())) {
784         myFocusTrackback.setWillBeScheduledForRestore();
785         IdeFocusManager mgr = getFocusManager();
786         Runnable r = () -> {
787           if (myFocusTrackback != null)  myFocusTrackback.restoreFocus();
788           myFocusTrackback = null;
789         };
790         mgr.doWhenFocusSettlesDown(r);
791       }
792     }
793
794     @Override
795     public void dispose() {
796       if (isShowing()) {
797         hide();
798       }
799
800       if (myWindowListener != null) {
801         myWindowListener.saveSize();
802         removeWindowListener(myWindowListener);
803         myWindowListener = null;
804       }
805
806       DialogWrapper.cleanupWindowListeners(this);
807
808       if (myFocusTrackback != null && !(myFocusTrackback.isScheduledForRestore() || myFocusTrackback.isWillBeScheduledForRestore())) {
809         myFocusTrackback.dispose();
810         myFocusTrackback = null;
811       }
812
813
814       final BufferStrategy strategy = getBufferStrategy();
815       if (strategy != null) {
816         strategy.dispose();
817       }
818       super.dispose();
819
820       removeAll();
821       DialogWrapper.cleanupRootPane(rootPane);
822       rootPane = null;
823
824       // http://bugs.sun.com/view_bug.do?bug_id=6614056
825       try {
826         synchronized (getTreeLock()) {
827           List<?> list = ReflectionUtil.getStaticFieldValue(Dialog.class, List.class, "modalDialogs");
828           list.remove(this);
829         }
830       }
831       catch (final Exception ignored) {
832       }
833     }
834
835     @Override
836     public Component getMostRecentFocusOwner() {
837       if (!myOpened) {
838         final DialogWrapper wrapper = getDialogWrapper();
839         if (wrapper != null) {
840           JComponent toFocus = wrapper.getPreferredFocusedComponent();
841           if (toFocus != null) {
842             return toFocus;
843           }
844         }
845       }
846       return super.getMostRecentFocusOwner();
847     }
848
849     @Override
850     public void paint(Graphics g) {
851       if (!SystemInfo.isMac || UIUtil.isUnderAquaLookAndFeel()) {  // avoid rendering problems with non-aqua (alloy) LaFs under mac
852         // actually, it's a bad idea to globally enable this for dialog graphics since renderers, for example, may not
853         // inherit graphics so rendering hints won't be applied and trees or lists may render ugly.
854         UISettings.setupAntialiasing(g);
855       }
856
857       super.paint(g);
858     }
859
860     @SuppressWarnings("SSBasedInspection")
861     private class MyWindowListener extends WindowAdapter {
862       @Override
863       public void windowClosing(WindowEvent e) {
864         DialogWrapper dialogWrapper = getDialogWrapper();
865         if (dialogWrapper.shouldCloseOnCross()) {
866           dialogWrapper.doCancelAction(e);
867         }
868       }
869
870       @Override
871       public void windowClosed(WindowEvent e) {
872         saveSize();
873       }
874
875       public void saveSize() {
876         if (myDimensionServiceKey != null &&
877             myInitialSize != null &&
878             myOpened) { // myInitialSize can be null only if dialog is disposed before first showing
879           final Project projectGuess = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(MyDialog.this));
880
881           // Save location
882           Point location = getLocation();
883           DimensionService.getInstance().setLocation(myDimensionServiceKey, location, projectGuess);
884           // Save size
885           Dimension size = getSize();
886           if (!myInitialSize.equals(size)) {
887             DimensionService.getInstance().setSize(myDimensionServiceKey, size, projectGuess);
888           }
889           myOpened = false;
890         }
891       }
892
893       @Override
894       public void windowOpened(final WindowEvent e) {
895         if (SystemInfo.isMacOSLion) {
896           Window window = e.getWindow();
897           if (window instanceof Dialog) {
898             ID _native = MacUtil.findWindowForTitle(((Dialog)window).getTitle());
899             if (_native != null && _native.intValue() > 0) {
900               // see MacMainFrameDecorator
901               // NSCollectionBehaviorFullScreenAuxiliary = 1 << 8
902               Foundation.invoke(_native, "setCollectionBehavior:", 1 << 8);
903             }
904           }
905         }
906         SwingUtilities.invokeLater(() -> {
907           myOpened = true;
908           final DialogWrapper activeWrapper = getActiveWrapper();
909           for (JComponent c : UIUtil.uiTraverser(e.getWindow()).filter(JComponent.class)) {
910             GraphicsUtil.setAntialiasingType(c, AntialiasingType.getAAHintForSwingComponent());
911           }
912           if (activeWrapper == null) {
913             myFocusedCallback.setRejected();
914             myTypeAheadDone.setRejected();
915           }
916         });
917       }
918
919       @Override
920       public void windowActivated(final WindowEvent e) {
921         if (myFocusTrackback != null) {
922           DialogWrapper wrapper = getDialogWrapper();
923           if (wrapper != null) {
924             myFocusTrackback.cleanParentWindow();
925             myFocusTrackback.registerFocusComponent(new FocusTrackback.ComponentQuery() {
926               @Override
927               public Component getComponent() {
928                 return wrapper.getPreferredFocusedComponent();
929               }
930             });
931           }
932         }
933         SwingUtilities.invokeLater(() -> {
934           final DialogWrapper wrapper = getActiveWrapper();
935           if (wrapper == null && !myFocusedCallback.isProcessed()) {
936             myFocusedCallback.setRejected();
937             myTypeAheadDone.setRejected();
938             return;
939           }
940
941           if (myActivated) {
942             return;
943           }
944           myActivated = true;
945           JComponent toFocus = wrapper == null ? null : wrapper.getPreferredFocusedComponent();
946           if (getRootPane() != null && toFocus == null) {
947             toFocus = getRootPane().getDefaultButton();
948           }
949
950           if (getRootPane() != null) {
951             IJSwingUtilities.moveMousePointerOn(getRootPane().getDefaultButton());
952           }
953           setupSelectionOnPreferredComponent(toFocus);
954
955           if (toFocus != null) {
956             if (isShowing() && isActive()) {
957               getFocusManager().requestFocus(toFocus, true);
958               notifyFocused(wrapper);
959             }
960           } else {
961             if (isShowing()) {
962               notifyFocused(wrapper);
963             }
964           }
965           if (myTypeAheadCallback != null) {
966             myTypeAheadCallback.setDone();
967           }
968         });
969       }
970
971       @Override
972       public void windowDeactivated(WindowEvent e) {
973         if (!isModal()) {
974           Ref<IdeFocusManager> focusManager = new Ref<>(null);
975           Project project = getProject();
976           if (project != null && !project.isDisposed()) {
977             focusManager.set(getFocusManager());
978             focusManager.get().doWhenFocusSettlesDown(() -> disposeFocusTrackbackIfNoChildWindowFocused(focusManager.get()));
979           }
980           else {
981             disposeFocusTrackbackIfNoChildWindowFocused(focusManager.get());
982           }
983         }
984       }
985
986       private void notifyFocused(DialogWrapper wrapper) {
987         myFocusedCallback.setDone();
988         final long timeout = wrapper.getTypeAheadTimeoutMs();
989         if (timeout > 0) {
990           SimpleTimer.getInstance().setUp(new EdtRunnable() {
991             @Override
992             public void runEdt() {
993               myTypeAheadDone.setDone();
994             }
995           }, timeout);
996         }
997       }
998
999       private DialogWrapper getActiveWrapper() {
1000         DialogWrapper activeWrapper = getDialogWrapper();
1001         if (activeWrapper == null || !activeWrapper.isShowing()) {
1002           return null;
1003         }
1004
1005         return activeWrapper;
1006       }
1007     }
1008
1009     private class DialogRootPane extends JRootPane implements DataProvider {
1010
1011       private final boolean myGlassPaneIsSet;
1012
1013       private Dimension myLastMinimumSize;
1014
1015       private DialogRootPane() {
1016         setGlassPane(new IdeGlassPaneImpl(this));
1017         myGlassPaneIsSet = true;
1018         putClientProperty("DIALOG_ROOT_PANE", true);
1019       }
1020
1021       @NotNull
1022       @Override
1023       protected JLayeredPane createLayeredPane() {
1024         JLayeredPane p = new JBLayeredPane();
1025         p.setName(this.getName()+".layeredPane");
1026         return p;
1027       }
1028
1029       @Override
1030       public void validate() {
1031         super.validate();
1032         DialogWrapper wrapper = myDialogWrapper.get();
1033         if (wrapper != null && wrapper.isAutoAdjustable()) {
1034           Window window = wrapper.getWindow();
1035           if (window != null) {
1036             Dimension size = getMinimumSize();
1037             if (!(size == null ? myLastMinimumSize == null : size.equals(myLastMinimumSize))) {
1038               // update window minimum size only if root pane minimum size is changed
1039               if (size == null) {
1040                 myLastMinimumSize = null;
1041               }
1042               else {
1043                 myLastMinimumSize = new Dimension(size);
1044                 JBInsets.addTo(size, window.getInsets());
1045                 Rectangle screen = ScreenUtil.getScreenRectangle(window);
1046                 if (size.width > screen.width || size.height > screen.height) {
1047                   Application application = ApplicationManager.getApplication();
1048                   if (application != null && application.isInternal()) {
1049                     LOG.warn("minimum size " + size.width + "x" + size.height +
1050                              " is bigger than screen " + screen.width + "x" + screen.height);
1051                   }
1052                   if (size.width > screen.width) size.width = screen.width;
1053                   if (size.height > screen.height) size.height = screen.height;
1054                 }
1055               }
1056               window.setMinimumSize(size);
1057             }
1058           }
1059         }
1060       }
1061
1062       @Override
1063       public void setGlassPane(final Component glass) {
1064         if (myGlassPaneIsSet) {
1065           LOG.warn("Setting of glass pane for DialogWrapper is prohibited", new Exception());
1066           return;
1067         }
1068
1069         super.setGlassPane(glass);
1070       }
1071
1072       @Override
1073       public void setContentPane(Container contentPane) {
1074         super.setContentPane(contentPane);
1075         if (contentPane != null) {
1076           contentPane.addMouseMotionListener(new MouseMotionAdapter() {}); // listen to mouse motino events for a11y
1077         }
1078       }
1079
1080       @Override
1081       public Object getData(@NonNls String dataId) {
1082         final DialogWrapper wrapper = myDialogWrapper.get();
1083         return wrapper != null && PlatformDataKeys.UI_DISPOSABLE.is(dataId) ? wrapper.getDisposable() : null;
1084       }
1085     }
1086   }
1087
1088   private static void setupSelectionOnPreferredComponent(final JComponent component) {
1089     if (component instanceof JTextField) {
1090       JTextField field = (JTextField)component;
1091       String text = field.getText();
1092       if (text != null && field.getClientProperty(HAVE_INITIAL_SELECTION) == null) {
1093         field.setSelectionStart(0);
1094         field.setSelectionEnd(text.length());
1095       }
1096     }
1097     else if (component instanceof JComboBox) {
1098       JComboBox combobox = (JComboBox)component;
1099       combobox.getEditor().selectAll();
1100     }
1101   }
1102
1103   @Override
1104   public void setContentPane(JComponent content) {
1105     myDialog.setContentPane(content);
1106   }
1107
1108   @Override
1109   public void centerInParent() {
1110     myDialog.centerInParent();
1111   }
1112
1113   public void setAutoRequestFocus(boolean b) {
1114     UIUtil.setAutoRequestFocus((JDialog)myDialog, b);
1115   }
1116 }