dialog creation support for dock containers
[idea/community.git] / platform / platform-impl / src / com / intellij / openapi / ui / FrameWrapper.java
1 /*
2  * Copyright 2000-2009 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;
17
18 import com.intellij.ide.DataManager;
19 import com.intellij.openapi.Disposable;
20 import com.intellij.openapi.actionSystem.DataProvider;
21 import com.intellij.openapi.actionSystem.PlatformDataKeys;
22 import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
23 import com.intellij.openapi.actionSystem.impl.MouseGestureManager;
24 import com.intellij.openapi.application.impl.ApplicationInfoImpl;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.project.ProjectManager;
27 import com.intellij.openapi.project.ProjectManagerAdapter;
28 import com.intellij.openapi.project.ProjectManagerListener;
29 import com.intellij.openapi.util.ActionCallback;
30 import com.intellij.openapi.util.DimensionService;
31 import com.intellij.openapi.util.Disposer;
32 import com.intellij.openapi.util.SystemInfo;
33 import com.intellij.openapi.wm.*;
34 import com.intellij.openapi.wm.ex.LayoutFocusTraversalPolicyExt;
35 import com.intellij.openapi.wm.ex.WindowManagerEx;
36 import com.intellij.openapi.wm.impl.IdeFrameImpl;
37 import com.intellij.openapi.wm.impl.IdeGlassPaneImpl;
38 import com.intellij.openapi.wm.impl.IdeMenuBar;
39 import com.intellij.ui.FocusTrackback;
40 import com.intellij.util.ImageLoader;
41 import com.intellij.util.containers.HashMap;
42 import com.intellij.util.ui.UIUtil;
43 import org.jetbrains.annotations.NonNls;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
46
47 import javax.swing.*;
48 import java.awt.*;
49 import java.awt.event.*;
50 import java.io.File;
51 import java.util.Map;
52
53 public class FrameWrapper implements Disposable, DataProvider {
54   private String myDimensionKey = null;
55   private JComponent myComponent = null;
56   private JComponent myPreferedFocus = null;
57   private String myTitle = "";
58   private Image myImage = ImageLoader.loadFromResource(ApplicationInfoImpl.getShadowInstance().getIconUrl());
59   private boolean myCloseOnEsc = false;
60   private Window myFrame;
61   private final Map<String, Object> myDatas = new HashMap<String, Object>();
62   private Project myProject;
63   private final ProjectManagerListener myProjectListener = new MyProjectManagerListener();
64   private FocusTrackback myFocusTrackback;
65   private FocusWatcher myFocusWatcher;
66
67   private ActionCallback myFocusedCallback;
68   private boolean myDisposed;
69
70   protected StatusBar myStatusBar;
71   private boolean myShown;
72   private boolean myIsDialog;
73
74   public FrameWrapper(Project project) {
75     this(project, null);
76   }
77
78   public FrameWrapper(Project project, @Nullable @NonNls String dimensionServiceKey) {
79     this(project, dimensionServiceKey, false);
80   }
81
82   public FrameWrapper(Project project, @Nullable @NonNls String dimensionServiceKey, boolean isDialog) {
83     myProject = project;
84     myDimensionKey = dimensionServiceKey;
85     myIsDialog = isDialog;
86   }
87
88   public void setDimensionKey(String dimensionKey) {
89     myDimensionKey = dimensionKey;
90   }
91
92   public void setData(String dataId, Object data) {
93     myDatas.put(dataId, data);
94   }
95
96   public void setProject(@NotNull final Project project) {
97     myProject = project;
98     setData(PlatformDataKeys.PROJECT.getName(), project);
99     ProjectManager.getInstance().addProjectManagerListener(project, myProjectListener);
100     Disposer.register(this, new Disposable() {
101       @Override
102       public void dispose() {
103         ProjectManager.getInstance().removeProjectManagerListener(project, myProjectListener);
104       }
105     });
106   }
107
108   public void show() {
109     show(true);
110   }
111
112   public void show(boolean restoreBounds) {
113     myFocusedCallback = new ActionCallback();
114
115     if (myProject != null) {
116       IdeFocusManager.getInstance(myProject).typeAheadUntil(myFocusedCallback);
117     }
118
119     final Window frame = getFrame();
120
121     if (myStatusBar != null) {
122       myStatusBar.install((IdeFrame)frame);
123     }
124
125     myFocusTrackback = new FocusTrackback(this, IdeFocusManager.findInstance().getFocusOwner(), true);
126
127     if (frame instanceof JFrame) {
128       ((JFrame)frame).setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
129     } else {
130       ((JDialog)frame).setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
131     }
132     WindowAdapter focusListener = new WindowAdapter() {
133       public void windowOpened(WindowEvent e) {
134         IdeFocusManager fm = IdeFocusManager.getInstance(myProject);
135         JComponent toFocus = myPreferedFocus;
136         if (toFocus == null) {
137           toFocus = fm.getFocusTargetFor(myComponent);
138         }
139
140         if (toFocus != null) {
141           fm.requestFocus(toFocus, true).notify(myFocusedCallback);
142         } else {
143           myFocusedCallback.setRejected();
144         }
145       }
146     };
147     frame.addWindowListener(focusListener);
148     if (myCloseOnEsc) addCloseOnEsc((RootPaneContainer)frame);
149     ((RootPaneContainer)frame).getContentPane().add(myComponent, BorderLayout.CENTER);
150     if (frame instanceof JFrame) {
151       ((JFrame)frame).setTitle(myTitle);
152     } else {
153       ((JDialog)frame).setTitle(myTitle);
154     }
155     frame.setIconImage(myImage);
156
157     if (restoreBounds) {
158       loadFrameState();
159     }
160
161     myFocusWatcher = new FocusWatcher() {
162       protected void focusLostImpl(final FocusEvent e) {
163         myFocusTrackback.consume();
164       }
165     };
166     myFocusWatcher.install(myComponent);
167     myShown = true;
168     frame.setVisible(true);
169
170     if (UIUtil.isUnderAlloyLookAndFeel() && frame instanceof JFrame) {
171       //please ask [kb] before remove it
172       ((JFrame)frame).setMaximizedBounds(null);
173     }
174   }
175
176   public void close() {
177     Disposer.dispose(this);
178   }
179
180   public void dispose() {
181     if (isDisposed()) return;
182
183     Window frame = getFrame();
184     frame.setVisible(false);
185     frame.dispose();
186
187     if (frame instanceof JFrame) {
188       FocusTrackback.release((JFrame)frame);
189     }
190
191     if (myStatusBar != null) {
192       Disposer.dispose(myStatusBar);
193       myStatusBar = null;
194     }
195
196     myDisposed = true;
197   }
198
199   public boolean isDisposed() {
200     return myDisposed;
201   }
202
203   private void addCloseOnEsc(final RootPaneContainer frame) {
204     frame.getRootPane().registerKeyboardAction(
205       new ActionListener() {
206         public void actionPerformed(ActionEvent e) {
207           MenuSelectionManager menuSelectionManager = MenuSelectionManager.defaultManager();
208           MenuElement[] selectedPath = menuSelectionManager.getSelectedPath();
209           if (selectedPath.length > 0) { // hide popup menu if any
210             menuSelectionManager.clearSelectedPath();
211           }
212           else {
213             close();
214           }
215         }
216       },
217       KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
218       JComponent.WHEN_IN_FOCUSED_WINDOW
219     );
220   }
221
222   public Window getFrame() {
223     assert !myDisposed : "Already disposed!";
224
225     if (myFrame == null) {
226       final IdeFrame parent = WindowManager.getInstance().getIdeFrame(myProject);
227       myFrame = myIsDialog ? createJDialog(parent) : createJFrame(parent);
228     }
229     return myFrame;
230   }
231
232   protected JFrame createJFrame(IdeFrame parent) {
233     return new MyJFrame(parent) {
234       @Override
235       public IdeRootPaneNorthExtension getNorthExtension(String key) {
236         return FrameWrapper.this.getNorthExtension(key);
237       }
238     };
239   }
240
241   protected JDialog createJDialog(IdeFrame parent) {
242     return new MyJDialog(parent);
243   }
244
245   protected IdeRootPaneNorthExtension getNorthExtension(String key) {
246     return null;
247   }
248
249   @Override
250   public Object getData(@NonNls String dataId) {
251     return null;
252   }
253
254   public void setComponent(JComponent component) {
255     myComponent = component;
256   }
257
258   public void setPreferredFocusedComponent(JComponent preferedFocus) {
259     myPreferedFocus = preferedFocus;
260   }
261
262   public void closeOnEsc() {
263     myCloseOnEsc = true;
264   }
265
266   public void setImage(Image image) {
267     myImage = image;
268   }
269
270   protected void loadFrameState() {
271     final Window frame = getFrame();
272     final Point location;
273     final Dimension size;
274     final int extendedState;
275     DimensionService dimensionService = DimensionService.getInstance();
276     if (myDimensionKey == null || dimensionService == null) {
277       location = null;
278       size = null;
279       extendedState = -1;
280     }
281     else {
282       location = dimensionService.getLocation(myDimensionKey);
283       size = dimensionService.getSize(myDimensionKey);
284       extendedState = dimensionService.getExtendedState(myDimensionKey);
285     }
286
287     if (size != null && location != null) {
288       frame.setLocation(location);
289       frame.setSize(size);
290       ((RootPaneContainer)frame).getRootPane().revalidate();
291     }
292     else {
293       frame.pack();
294       frame.setBounds(WindowManagerEx.getInstanceEx().getIdeFrame(myProject).suggestChildFrameBounds());
295     }
296
297     if (extendedState == Frame.ICONIFIED || extendedState == Frame.MAXIMIZED_BOTH && frame instanceof JFrame) {
298       ((JFrame)frame).setExtendedState(extendedState);
299     }
300   }
301
302   private static void saveFrameState(String dimensionKey, Component frame) {
303     DimensionService dimensionService = DimensionService.getInstance();
304     if (dimensionKey == null || dimensionService == null) return;
305     dimensionService.setLocation(dimensionKey, frame.getLocation());
306     dimensionService.setSize(dimensionKey, frame.getSize());
307     if (frame instanceof JFrame) {
308       dimensionService.setExtendedState(dimensionKey, ((JFrame)frame).getExtendedState());
309     }
310   }
311
312   public void setTitle(String title) {
313     myTitle = title;
314   }
315
316   public void addDisposable(Disposable disposable) {
317     Disposer.register(this, disposable);
318   }
319
320   protected void setStatusBar(StatusBar statusBar) {
321     if (myStatusBar != null) {
322       Disposer.dispose(myStatusBar);
323     }
324     myStatusBar = statusBar;
325   }
326
327   private class MyJFrame extends JFrame implements DataProvider, IdeFrame.Child {
328
329     private boolean myDisposing;
330     private final IdeFrame myParent;
331
332     private String myFrameTitle;
333     private String myFileTitle;
334     private File myFile;
335
336     private MyJFrame(IdeFrame parent) throws HeadlessException {
337       myParent = parent;
338       setGlassPane(new IdeGlassPaneImpl(getRootPane()));
339
340       if (SystemInfo.isMac) {
341         setJMenuBar(new IdeMenuBar(ActionManagerEx.getInstanceEx(), DataManager.getInstance()));
342       }
343
344       MouseGestureManager.getInstance().add(this);
345       setFocusTraversalPolicy(new LayoutFocusTraversalPolicyExt());
346     }
347
348     @Override
349     public JComponent getComponent() {
350       return getRootPane();
351     }
352
353     @Override
354     public StatusBar getStatusBar() {
355       return myStatusBar != null ? myStatusBar : myParent.getStatusBar();
356     }
357
358     @Override
359     public Rectangle suggestChildFrameBounds() {
360       return myParent.suggestChildFrameBounds();
361     }
362
363     @Override
364     public Project getProject() {
365       return myParent.getProject();
366     }
367
368     @Override
369     public void setFrameTitle(String title) {
370       myFrameTitle = title;
371       updateTitle();
372     }
373
374     @Override
375     public void setFileTitle(String fileTitle, File ioFile) {
376       myFileTitle = fileTitle;
377       myFile = ioFile;
378       updateTitle();
379     }
380
381     @Override
382     public IdeRootPaneNorthExtension getNorthExtension(String key) {
383       return null;
384     }
385
386
387     private void updateTitle() {
388       IdeFrameImpl.updateTitle(this, myFrameTitle, myFileTitle, myFile);
389     }
390
391     @Override
392     public IdeFrame getParentFrame() {
393       return myParent;
394     }
395
396     public void dispose() {
397       if (myDisposing) return;
398       myDisposing = true;
399
400       MouseGestureManager.getInstance().remove(this);
401
402       if (myShown) {
403         saveFrameState(myDimensionKey, this);
404       }
405
406       Disposer.dispose(FrameWrapper.this);
407       myDatas.clear();
408       myProject = null;
409       myPreferedFocus = null;
410
411       if (myFocusTrackback != null) {
412         myFocusTrackback.restoreFocus();
413       }
414       if (myComponent != null && myFocusWatcher != null) {
415         myFocusWatcher.deinstall(myComponent);
416       }
417       myFocusWatcher = null;
418       myFocusedCallback = null;
419
420       super.dispose();
421     }
422
423     public Object getData(String dataId) {
424       if (IdeFrame.KEY.getName().equals(dataId)) {
425         return this;
426       }
427
428       Object data = FrameWrapper.this.getData(dataId);
429       return data != null ? data : myDatas.get(dataId);
430     }
431
432     @Override
433     public void paint(Graphics g) {
434       UIUtil.applyRenderingHints(g);
435       super.paint(g);
436     }
437   }
438
439   private class MyJDialog extends JDialog implements DataProvider, IdeFrame.Child {
440
441     private boolean myDisposing;
442     private final IdeFrame myParent;
443
444     private MyJDialog(IdeFrame parent) throws HeadlessException {
445       super((JFrame)parent);
446       myParent = parent;
447       setGlassPane(new IdeGlassPaneImpl(getRootPane()));
448
449       MouseGestureManager.getInstance().add(this);
450       setFocusTraversalPolicy(new LayoutFocusTraversalPolicyExt());
451     }
452
453     @Override
454     public JComponent getComponent() {
455       return getRootPane();
456     }
457
458     @Override
459     public StatusBar getStatusBar() {
460       return null;
461     }
462
463     @Override
464     public Rectangle suggestChildFrameBounds() {
465       return myParent.suggestChildFrameBounds();
466     }
467
468     @Override
469     public Project getProject() {
470       return myParent.getProject();
471     }
472
473     @Override
474     public void setFrameTitle(String title) {
475       setTitle(title);
476     }
477
478     @Override
479     public void setFileTitle(String fileTitle, File ioFile) {
480       setTitle(fileTitle);
481     }
482
483     @Override
484     public IdeRootPaneNorthExtension getNorthExtension(String key) {
485       return null;
486     }
487
488     @Override
489     public IdeFrame getParentFrame() {
490       return myParent;
491     }
492
493     public void dispose() {
494       if (myDisposing) return;
495       myDisposing = true;
496
497       MouseGestureManager.getInstance().remove(this);
498
499       if (myShown) {
500         saveFrameState(myDimensionKey, this);
501       }
502
503       Disposer.dispose(FrameWrapper.this);
504       myDatas.clear();
505       myProject = null;
506       myPreferedFocus = null;
507
508       if (myFocusTrackback != null) {
509         myFocusTrackback.restoreFocus();
510       }
511       if (myComponent != null && myFocusWatcher != null) {
512         myFocusWatcher.deinstall(myComponent);
513       }
514       myFocusWatcher = null;
515       myFocusedCallback = null;
516
517       super.dispose();
518     }
519
520     public Object getData(String dataId) {
521       if (IdeFrame.KEY.getName().equals(dataId)) {
522         return this;
523       }
524
525       Object data = FrameWrapper.this.getData(dataId);
526       return data != null ? data : myDatas.get(dataId);
527     }
528
529     @Override
530     public void paint(Graphics g) {
531       UIUtil.applyRenderingHints(g);
532       super.paint(g);
533     }
534   }
535
536
537   public void setLocation(Point location) {
538     getFrame().setLocation(location);
539   }
540
541   public void setSize(Dimension size) {
542     getFrame().setSize(size);
543   }
544
545   private class MyProjectManagerListener extends ProjectManagerAdapter {
546     public void projectClosing(Project project) {
547       if (project == myProject) {
548         close();
549       }
550     }
551   }
552 }