f45e1bcd8aa25a46701d96cf874f323ed9e13c90
[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 JFrame 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
73   public FrameWrapper(Project project) {
74     this(project, null);
75   }
76
77   public FrameWrapper(Project project, @Nullable @NonNls String dimensionServiceKey) {
78     myProject = project;
79     myDimensionKey = dimensionServiceKey;
80   }
81
82   public void setDimensionKey(String dimensionKey) {
83     myDimensionKey = dimensionKey;
84   }
85
86   public void setData(String dataId, Object data) {
87     myDatas.put(dataId, data);
88   }
89
90   public void setProject(@NotNull final Project project) {
91     myProject = project;
92     setData(PlatformDataKeys.PROJECT.getName(), project);
93     ProjectManager.getInstance().addProjectManagerListener(project, myProjectListener);
94     Disposer.register(this, new Disposable() {
95       @Override
96       public void dispose() {
97         ProjectManager.getInstance().removeProjectManagerListener(project, myProjectListener);
98       }
99     });
100   }
101
102   public void show() {
103     show(true);
104   }
105
106   public void show(boolean restoreBounds) {
107     myFocusedCallback = new ActionCallback();
108
109     if (myProject != null) {
110       IdeFocusManager.getInstance(myProject).typeAheadUntil(myFocusedCallback);
111     }
112
113     final JFrame frame = getFrame();
114
115     if (myStatusBar != null) {
116       myStatusBar.install((IdeFrame)frame);
117     }
118
119     myFocusTrackback = new FocusTrackback(this, IdeFocusManager.findInstance().getFocusOwner(), true);
120
121     frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
122     WindowAdapter focusListener = new WindowAdapter() {
123       public void windowOpened(WindowEvent e) {
124         IdeFocusManager fm = IdeFocusManager.getInstance(myProject);
125         JComponent toFocus = myPreferedFocus;
126         if (toFocus == null) {
127           toFocus = fm.getFocusTargetFor(myComponent);
128         }
129
130         if (toFocus != null) {
131           fm.requestFocus(toFocus, true).notify(myFocusedCallback);
132         } else {
133           myFocusedCallback.setRejected();
134         }
135       }
136     };
137     frame.addWindowListener(focusListener);
138     if (myCloseOnEsc) addCloseOnEsc(frame);
139     frame.getContentPane().add(myComponent, BorderLayout.CENTER);
140     frame.setTitle(myTitle);
141     frame.setIconImage(myImage);
142
143     if (restoreBounds) {
144       loadFrameState();
145     }
146
147     myFocusWatcher = new FocusWatcher() {
148       protected void focusLostImpl(final FocusEvent e) {
149         myFocusTrackback.consume();
150       }
151     };
152     myFocusWatcher.install(myComponent);
153     myShown = true;
154     frame.setVisible(true);
155
156     if (UIUtil.isUnderAlloyLookAndFeel()) {
157       //please ask [kb] before remove it
158       frame.setMaximizedBounds(null);
159     }
160   }
161
162   public void close() {
163     Disposer.dispose(this);
164   }
165
166   public void dispose() {
167     if (isDisposed()) return;
168
169     JFrame frame = getFrame();
170     frame.setVisible(false);
171     frame.dispose();
172
173     FocusTrackback.release(frame);
174
175     if (myStatusBar != null) {
176       Disposer.dispose(myStatusBar);
177       myStatusBar = null;
178     }
179
180     myDisposed = true;
181   }
182
183   public boolean isDisposed() {
184     return myDisposed;
185   }
186
187   private void addCloseOnEsc(final JFrame frame) {
188     frame.getRootPane().registerKeyboardAction(
189       new ActionListener() {
190         public void actionPerformed(ActionEvent e) {
191           MenuSelectionManager menuSelectionManager = MenuSelectionManager.defaultManager();
192           MenuElement[] selectedPath = menuSelectionManager.getSelectedPath();
193           if (selectedPath.length > 0) { // hide popup menu if any
194             menuSelectionManager.clearSelectedPath();
195           }
196           else {
197             close();
198           }
199         }
200       },
201       KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
202       JComponent.WHEN_IN_FOCUSED_WINDOW
203     );
204   }
205
206   public JFrame getFrame() {
207     assert !myDisposed : "Already disposed!";
208
209     if (myFrame == null) {
210       myFrame = createJFrame(WindowManager.getInstance().getIdeFrame(myProject));
211     }
212     return myFrame;
213   }
214
215   protected JFrame createJFrame(IdeFrame parent) {
216     return new MyJFrame(parent) {
217       @Override
218       public IdeRootPaneNorthExtension getNorthExtension(String key) {
219         return FrameWrapper.this.getNorthExtension(key);
220       }
221     };
222   }
223
224   protected IdeRootPaneNorthExtension getNorthExtension(String key) {
225     return null;
226   }
227
228   @Override
229   public Object getData(@NonNls String dataId) {
230     return null;
231   }
232
233   public void setComponent(JComponent component) {
234     myComponent = component;
235   }
236
237   public void setPreferredFocusedComponent(JComponent preferedFocus) {
238     myPreferedFocus = preferedFocus;
239   }
240
241   public void closeOnEsc() {
242     myCloseOnEsc = true;
243   }
244
245   public void setImage(Image image) {
246     myImage = image;
247   }
248
249   protected void loadFrameState() {
250     final JFrame frame = getFrame();
251     final Point location;
252     final Dimension size;
253     final int extendedState;
254     DimensionService dimensionService = DimensionService.getInstance();
255     if (myDimensionKey == null || dimensionService == null) {
256       location = null;
257       size = null;
258       extendedState = -1;
259     }
260     else {
261       location = dimensionService.getLocation(myDimensionKey);
262       size = dimensionService.getSize(myDimensionKey);
263       extendedState = dimensionService.getExtendedState(myDimensionKey);
264     }
265
266     if (size != null && location != null) {
267       frame.setLocation(location);
268       frame.setSize(size);
269       frame.getRootPane().revalidate();
270     }
271     else {
272       frame.pack();
273       frame.setBounds(WindowManagerEx.getInstanceEx().getIdeFrame(myProject).suggestChildFrameBounds());
274     }
275
276     if (extendedState == Frame.ICONIFIED || extendedState == Frame.MAXIMIZED_BOTH) {
277       frame.setExtendedState(extendedState);
278     }
279   }
280
281   private static void saveFrameState(String dimensionKey, JFrame frame) {
282     DimensionService dimensionService = DimensionService.getInstance();
283     if (dimensionKey == null || dimensionService == null) return;
284     dimensionService.setLocation(dimensionKey, frame.getLocation());
285     dimensionService.setSize(dimensionKey, frame.getSize());
286     dimensionService.setExtendedState(dimensionKey, frame.getExtendedState());
287   }
288
289   public void setTitle(String title) {
290     myTitle = title;
291   }
292
293   public void addDisposable(Disposable disposable) {
294     Disposer.register(this, disposable);
295   }
296
297   protected void setStatusBar(StatusBar statusBar) {
298     if (myStatusBar != null) {
299       Disposer.dispose(myStatusBar);
300     }
301     myStatusBar = statusBar;
302   }
303
304   private class MyJFrame extends JFrame implements DataProvider, IdeFrame.Child {
305
306     private boolean myDisposing;
307     private final IdeFrame myParent;
308
309     private String myFrameTitle;
310     private String myFileTitle;
311     private File myFile;
312
313     private MyJFrame(IdeFrame parent) throws HeadlessException {
314       myParent = parent;
315       setGlassPane(new IdeGlassPaneImpl(getRootPane()));
316
317       if (SystemInfo.isMac) {
318         setJMenuBar(new IdeMenuBar(ActionManagerEx.getInstanceEx(), DataManager.getInstance()));
319       }
320
321       MouseGestureManager.getInstance().add(this);
322       setFocusTraversalPolicy(new LayoutFocusTraversalPolicyExt());
323     }
324
325     @Override
326     public JComponent getComponent() {
327       return getRootPane();
328     }
329
330     @Override
331     public StatusBar getStatusBar() {
332       return myStatusBar != null ? myStatusBar : myParent.getStatusBar();
333     }
334
335     @Override
336     public Rectangle suggestChildFrameBounds() {
337       return myParent.suggestChildFrameBounds();
338     }
339
340     @Override
341     public Project getProject() {
342       return myParent.getProject();
343     }
344
345     @Override
346     public void setFrameTitle(String title) {
347       myFrameTitle = title;
348       updateTitle();
349     }
350
351     @Override
352     public void setFileTitle(String fileTitle, File ioFile) {
353       myFileTitle = fileTitle;
354       myFile = ioFile;
355       updateTitle();
356     }
357
358     @Override
359     public IdeRootPaneNorthExtension getNorthExtension(String key) {
360       return null;
361     }
362
363
364     private void updateTitle() {
365       IdeFrameImpl.updateTitle(this, myFrameTitle, myFileTitle, myFile);
366     }
367
368     @Override
369     public IdeFrame getParentFrame() {
370       return myParent;
371     }
372
373     public void dispose() {
374       if (myDisposing) return;
375       myDisposing = true;
376
377       MouseGestureManager.getInstance().remove(this);
378
379       if (myShown) {
380         saveFrameState(myDimensionKey, this);
381       }
382
383       Disposer.dispose(FrameWrapper.this);
384       myDatas.clear();
385       myProject = null;
386       myPreferedFocus = null;
387
388       if (myFocusTrackback != null) {
389         myFocusTrackback.restoreFocus();
390       }
391       if (myComponent != null && myFocusWatcher != null) {
392         myFocusWatcher.deinstall(myComponent);
393       }
394       myFocusWatcher = null;
395       myFocusedCallback = null;
396
397       super.dispose();
398     }
399
400     public Object getData(String dataId) {
401       if (IdeFrame.KEY.getName().equals(dataId)) {
402         return this;
403       }
404
405       Object data = FrameWrapper.this.getData(dataId);
406       return data != null ? data : myDatas.get(dataId);
407     }
408
409     @Override
410     public void paint(Graphics g) {
411       UIUtil.applyRenderingHints(g);
412       super.paint(g);
413     }
414   }
415
416   public void setLocation(Point location) {
417     getFrame().setLocation(location);
418   }
419
420   public void setSize(Dimension size) {
421     getFrame().setSize(size);
422   }
423
424   private class MyProjectManagerListener extends ProjectManagerAdapter {
425     public void projectClosing(Project project) {
426       if (project == myProject) {
427         close();
428       }
429     }
430   }
431 }