2 * Copyright 2000-2010 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.
16 package com.intellij.ui.docking.impl;
18 import com.intellij.ide.IdeEventQueue;
19 import com.intellij.ide.ui.UISettings;
20 import com.intellij.ide.ui.UISettingsListener;
21 import com.intellij.openapi.Disposable;
22 import com.intellij.openapi.components.PersistentStateComponent;
23 import com.intellij.openapi.components.State;
24 import com.intellij.openapi.components.Storage;
25 import com.intellij.openapi.extensions.ExtensionPointListener;
26 import com.intellij.openapi.extensions.Extensions;
27 import com.intellij.openapi.extensions.PluginDescriptor;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.ui.FrameWrapper;
30 import com.intellij.openapi.util.ActionCallback;
31 import com.intellij.openapi.util.BusyObject;
32 import com.intellij.openapi.util.Disposer;
33 import com.intellij.openapi.util.MutualMap;
34 import com.intellij.openapi.wm.IdeFrame;
35 import com.intellij.openapi.wm.IdeRootPaneNorthExtension;
36 import com.intellij.openapi.wm.WindowManager;
37 import com.intellij.openapi.wm.ex.WindowManagerEx;
38 import com.intellij.openapi.wm.impl.IdeFocusManagerImpl;
39 import com.intellij.ui.ScreenUtil;
40 import com.intellij.ui.awt.RelativePoint;
41 import com.intellij.ui.awt.RelativeRectangle;
42 import com.intellij.ui.components.panels.NonOpaquePanel;
43 import com.intellij.ui.components.panels.VerticalBox;
44 import com.intellij.ui.docking.*;
45 import com.intellij.ui.tabs.impl.JBTabsImpl;
46 import com.intellij.util.ui.UIUtil;
47 import com.intellij.util.ui.update.UiNotifyConnector;
48 import org.jdom.Element;
49 import org.jetbrains.annotations.NotNull;
50 import org.jetbrains.annotations.Nullable;
53 import javax.swing.border.LineBorder;
55 import java.awt.event.KeyEvent;
56 import java.awt.event.MouseEvent;
57 import java.awt.event.WindowAdapter;
58 import java.awt.event.WindowEvent;
59 import java.awt.image.BufferedImage;
61 import java.util.List;
66 file = "$WORKSPACE_FILE$")})
68 public class DockManagerImpl extends DockManager implements PersistentStateComponent<Element>{
70 private Project myProject;
72 private Map<String, DockContainerFactory> myFactories = new HashMap<String, DockContainerFactory>();
74 private Set<DockContainer> myContainers = new HashSet<DockContainer>();
76 private MutualMap<DockContainer, DockWindow> myWindows = new MutualMap<DockContainer, DockWindow>();
78 private MyDragSession myCurrentDragSession;
80 private BusyObject.Impl myBusyObject = new BusyObject.Impl() {
82 public boolean isReady() {
83 return myCurrentDragSession == null;
87 private int myWindowIdCounter = 1;
89 private Element myLoadedState;
91 public DockManagerImpl(Project project) {
95 public void register(final DockContainer container) {
96 myContainers.add(container);
97 Disposer.register(container, new Disposable() {
99 public void dispose() {
100 myContainers.remove(container);
106 public void register(final String id, DockContainerFactory factory) {
107 myFactories.put(id, factory);
108 Disposer.register(factory, new Disposable() {
110 public void dispose() {
111 myFactories.remove(id);
119 public Set<DockContainer> getContainers() {
120 return Collections.unmodifiableSet(myContainers);
124 public IdeFrame getIdeFrame(DockContainer container) {
125 Component parent = UIUtil.findUltimateParent(container.getContainerComponent());
126 if (parent instanceof IdeFrame) {
127 return (IdeFrame)parent;
133 public String getDimensionKeyForFocus(@NotNull String key) {
134 Component owner = IdeFocusManagerImpl.getInstance(myProject).getFocusOwner();
135 if (owner == null) return key;
137 DockWindow wnd = myWindows.getValue(getContainerFor(owner));
139 return wnd != null ? key + "#" + wnd.myId : key;
142 public DockContainer getContainerFor(Component c) {
143 if (c == null) return null;
145 for (DockContainer eachContainer : myContainers) {
146 if (SwingUtilities.isDescendingFrom(c, eachContainer.getContainerComponent())) {
147 return eachContainer;
151 Component parent = UIUtil.findUltimateParent(c);
152 if (parent == null) return null;
154 for (DockContainer eachContainer : myContainers) {
155 if (parent == UIUtil.findUltimateParent(eachContainer.getContainerComponent())) {
156 return eachContainer;
164 public DragSession createDragSession(MouseEvent mouseEvent, DockableContent content) {
165 stopCurrentDragSession();
167 for (DockContainer each : myContainers) {
168 if (each.isEmpty() && each.isDisposeWhenEmpty()) {
169 DockWindow window = myWindows.getValue(each);
170 if (window != null) {
171 window.setTransparrent(true);
176 myCurrentDragSession = new MyDragSession(mouseEvent, content);
177 return myCurrentDragSession;
181 private void stopCurrentDragSession() {
182 if (myCurrentDragSession != null) {
183 myCurrentDragSession.cancel();
184 myCurrentDragSession = null;
185 myBusyObject.onReady();
187 for (DockContainer each : myContainers) {
188 if (!each.isEmpty()) {
189 DockWindow window = myWindows.getValue(each);
190 if (window != null) {
191 window.setTransparrent(false);
198 private ActionCallback getReady() {
199 return myBusyObject.getReady(this);
203 public void projectOpened() {
207 public void projectClosed() {
212 public String getComponentName() {
213 return "DockManager";
217 public void initComponent() {
221 public void disposeComponent() {
224 private class MyDragSession implements DragSession {
226 private JWindow myWindow;
228 private Image myDragImage;
229 private Image myDefaultDragImage;
231 private DockableContent myContent;
233 private DockContainer myCurrentOverContainer;
234 private JLabel myImageContainer;
236 private MyDragSession(MouseEvent me, DockableContent content) {
237 myWindow = new JWindow();
240 Image previewImage = content.getPreviewImage();
242 double requiredSize = 220;
244 double width = previewImage.getWidth(null);
245 double height = previewImage.getHeight(null);
248 if (width > height) {
249 ratio = requiredSize / width;
252 ratio = requiredSize / height;
255 BufferedImage buffer = new BufferedImage((int)width, (int)height, BufferedImage.TYPE_INT_ARGB);
256 buffer.createGraphics().drawImage(previewImage, 0, 0, (int)width, (int)height, null);
258 myDefaultDragImage = buffer.getScaledInstance((int)(width * ratio), (int)(height * ratio), Image.SCALE_SMOOTH);
259 myDragImage = myDefaultDragImage;
261 myWindow.getContentPane().setLayout(new BorderLayout());
262 myImageContainer = new JLabel(new ImageIcon(myDragImage));
263 myImageContainer.setBorder(new LineBorder(Color.lightGray));
264 myWindow.getContentPane().add(myImageContainer, BorderLayout.CENTER);
268 myWindow.setVisible(true);
270 WindowManagerEx.getInstanceEx().setAlphaModeEnabled(myWindow, true);
271 WindowManagerEx.getInstanceEx().setAlphaModeRatio(myWindow, 0.1f);
272 myWindow.getRootPane().putClientProperty("Window.shadow", Boolean.FALSE);
275 private void setLocationFrom(MouseEvent me) {
276 Point showPoint = me.getPoint();
277 SwingUtilities.convertPointToScreen(showPoint, me.getComponent());
279 showPoint.x -= myDragImage.getWidth(null) / 2;
281 myWindow.setBounds(new Rectangle(showPoint, new Dimension(myDragImage.getWidth(null), myDragImage.getHeight(null))));
285 public void process(MouseEvent e) {
286 RelativePoint point = new RelativePoint(e);
289 if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
290 DockContainer over = findContainerFor(point, myContent);
291 if (myCurrentOverContainer != null && myCurrentOverContainer != over) {
292 myCurrentOverContainer.resetDropOver(myContent);
293 myCurrentOverContainer = null;
296 if (myCurrentOverContainer == null && over != null) {
297 myCurrentOverContainer = over;
298 img = myCurrentOverContainer.startDropOver(myContent, point);
301 if (myCurrentOverContainer != null) {
302 img = myCurrentOverContainer.processDropOver(myContent, point);
306 img = myDefaultDragImage;
309 if (img != myDragImage) {
311 myImageContainer.setIcon(new ImageIcon(myDragImage));
317 else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
318 if (myCurrentOverContainer == null) {
319 createNewDockContainerFor(myContent, point);
320 stopCurrentDragSession();
322 myCurrentOverContainer.add(myContent, point);
323 stopCurrentDragSession();
328 public void cancel() {
331 if (myCurrentOverContainer != null) {
332 myCurrentOverContainer.resetDropOver(myContent);
333 myCurrentOverContainer = null;
339 private DockContainer findContainerFor(RelativePoint point, DockableContent content) {
340 for (DockContainer each : myContainers) {
341 RelativeRectangle rec = each.getAcceptArea();
342 if (rec.contains(point) && each.canAccept(content, point)) {
352 private DockContainerFactory getFactory(String type) {
353 assert myFactories.containsKey(type) : "No factory for content type=" + type;
354 return myFactories.get(type);
357 public void createNewDockContainerFor(DockableContent content, RelativePoint point) {
358 DockContainer container = getFactory(content.getDockContainerType()).createContainer(content);
361 final DockWindow window = createWindowFor(null, container);
363 Dimension size = content.getPreferredSize();
364 Point showPoint = point.getScreenPoint();
365 showPoint.x -= size.width / 2;
366 showPoint.y -= size.height / 2;
368 Rectangle target = new Rectangle(showPoint, size);
369 ScreenUtil.moveRectangleToFitTheScreen(target);
370 ScreenUtil.cropRectangleToFitTheScreen(target);
373 window.setLocation(target.getLocation());
374 window.myDockContentUiContainer.setPreferredSize(target.getSize());
377 window.getFrame().pack();
379 container.add(content, new RelativePoint(target.getLocation()));
381 SwingUtilities.invokeLater(new Runnable() {
384 window.myUiContainer.setPreferredSize(null);
389 private DockWindow createWindowFor(@Nullable String id, DockContainer container) {
390 String windowId = id != null ? id : String.valueOf(myWindowIdCounter++);
391 DockWindow window = new DockWindow(windowId, myProject, container, container instanceof DockContainer.Dialog);
392 window.setDimensionKey("dock-window-" + windowId);
393 myWindows.put(container, window);
397 private class DockWindow extends FrameWrapper implements IdeEventQueue.EventDispatcher {
400 private DockContainer myContainer;
402 private VerticalBox myNorthPanel = new VerticalBox();
403 private Map<String, IdeRootPaneNorthExtension> myNorthExtensions = new LinkedHashMap<String, IdeRootPaneNorthExtension>();
405 private NonOpaquePanel myUiContainer;
406 private NonOpaquePanel myDockContentUiContainer;
408 private DockWindow(String id, Project project, DockContainer container, boolean dialog) {
409 super(project, null, dialog);
411 myContainer = container;
414 if (!(container instanceof DockContainer.Dialog)) {
415 setStatusBar(WindowManager.getInstance().getStatusBar(project).createChild());
418 myUiContainer = new NonOpaquePanel(new BorderLayout());
420 NonOpaquePanel center = new NonOpaquePanel(new BorderLayout(0, 2));
421 if (UIUtil.isUnderAquaLookAndFeel()) {
422 center.setOpaque(true);
423 center.setBackground(JBTabsImpl.MAC_AQUA_BG_COLOR);
426 center.add(myNorthPanel, BorderLayout.NORTH);
428 myDockContentUiContainer = new NonOpaquePanel(new BorderLayout());
429 myDockContentUiContainer.add(myContainer.getContainerComponent(), BorderLayout.CENTER);
430 center.add(myDockContentUiContainer, BorderLayout.CENTER);
432 myUiContainer.add(center, BorderLayout.CENTER);
433 if (!(container instanceof DockContainer.Dialog)) {
434 myUiContainer.add(myStatusBar.getComponent(), BorderLayout.SOUTH);
437 setComponent(myUiContainer);
438 addDisposable(container);
440 IdeEventQueue.getInstance().addPostprocessor(this, this);
442 myContainer.addListener(new DockContainer.Listener.Adapter() {
444 public void contentRemoved(Object key) {
445 getReady().doWhenDone(new Runnable() {
448 if (myContainer.isEmpty()) {
456 Extensions.getArea(myProject).getExtensionPoint(IdeRootPaneNorthExtension.EP_NAME).addExtensionPointListener(
457 new ExtensionPointListener<IdeRootPaneNorthExtension>() {
459 public void extensionAdded(@NotNull IdeRootPaneNorthExtension extension, @Nullable PluginDescriptor pluginDescriptor) {
464 public void extensionRemoved(@NotNull IdeRootPaneNorthExtension extension, @Nullable PluginDescriptor pluginDescriptor) {
470 UISettings.getInstance().addUISettingsListener(new UISettingsListener() {
472 public void uiSettingsChanged(UISettings source) {
482 protected IdeRootPaneNorthExtension getNorthExtension(String key) {
483 return myNorthExtensions.get(key);
486 private void updateNorthPanel() {
487 myNorthPanel.setVisible(UISettings.getInstance().SHOW_NAVIGATION_BAR &&
488 !(myContainer instanceof DockContainer.Dialog));
490 IdeRootPaneNorthExtension[] extensions = Extensions.getArea(myProject).getExtensionPoint(IdeRootPaneNorthExtension.EP_NAME).getExtensions();
491 HashSet<String> processedKeys = new HashSet<String>();
492 for (IdeRootPaneNorthExtension each : extensions) {
493 processedKeys.add(each.getKey());
494 if (myNorthExtensions.containsKey(each.getKey())) continue;
495 IdeRootPaneNorthExtension toInstall = each.copy();
496 myNorthExtensions.put(toInstall.getKey(), toInstall);
497 myNorthPanel.add(toInstall.getComponent());
500 Iterator<String> existing = myNorthExtensions.keySet().iterator();
501 while (existing.hasNext()) {
502 String each = existing.next();
503 if (processedKeys.contains(each)) continue;
505 IdeRootPaneNorthExtension toRemove = myNorthExtensions.get(each);
506 myNorthPanel.remove(toRemove.getComponent());
508 Disposer.dispose(toRemove);
511 myNorthPanel.revalidate();
512 myNorthPanel.repaint();
515 public void setTransparrent(boolean transparrent) {
517 WindowManagerEx.getInstanceEx().setAlphaModeEnabled(getFrame(), true);
518 WindowManagerEx.getInstanceEx().setAlphaModeRatio(getFrame(), 0.5f);
520 WindowManagerEx.getInstanceEx().setAlphaModeEnabled(getFrame(), true);
521 WindowManagerEx.getInstanceEx().setAlphaModeRatio(getFrame(), 0f);
526 public void dispose() {
528 myWindows.remove(myContainer);
530 for (IdeRootPaneNorthExtension each : myNorthExtensions.values()) {
531 Disposer.dispose(each);
533 myNorthExtensions.clear();
537 public boolean dispatch(AWTEvent e) {
538 if (e instanceof KeyEvent) {
539 if (myCurrentDragSession != null) {
540 stopCurrentDragSession();
547 protected JFrame createJFrame(IdeFrame parent) {
548 JFrame frame = super.createJFrame(parent);
549 installListeners(frame);
555 protected JDialog createJDialog(IdeFrame parent) {
556 JDialog frame = super.createJDialog(parent);
557 installListeners(frame);
562 private void installListeners(Window frame) {
563 frame.addWindowListener(new WindowAdapter() {
565 public void windowClosing(WindowEvent e) {
566 myContainer.closeAll();
570 new UiNotifyConnector(((RootPaneContainer)frame).getContentPane(), myContainer);
576 public Element getState() {
577 Element root = new Element("DockManager");
578 for (DockContainer each : myContainers) {
579 DockWindow eachWindow = myWindows.getValue(each);
580 if (eachWindow != null) {
581 if (each instanceof DockContainer.Persistent) {
582 DockContainer.Persistent eachContainer = (DockContainer.Persistent)each;
583 Element eachWindowElement = new Element("window");
584 eachWindowElement.setAttribute("id", eachWindow.myId);
585 Element content = new Element("content");
586 content.setAttribute("type", eachContainer.getDockContainerType());
587 content.addContent(eachContainer.getState());
588 eachWindowElement.addContent(content);
590 root.addContent(eachWindowElement);
598 public void loadState(Element state) {
599 myLoadedState = state;
602 private void readStateFor(String type) {
603 if (myLoadedState == null) return;
605 List windows = myLoadedState.getChildren("window");
606 for (int i = 0; i < windows.size(); i++) {
607 Element eachWindow = (Element)windows.get(i);
608 if (eachWindow == null) continue;
610 String eachId = eachWindow.getAttributeValue("id");
612 Element eachContent = eachWindow.getChild("content");
613 if (eachContent == null) continue;
615 String eachType = eachContent.getAttributeValue("type");
616 if (eachType == null || !type.equals(eachType) || !myFactories.containsKey(eachType)) continue;
618 DockContainerFactory factory = myFactories.get(eachType);
619 if (!(factory instanceof DockContainerFactory.Persistent)) continue;
621 DockContainerFactory.Persistent persistentFactory = (DockContainerFactory.Persistent)factory;
622 DockContainer container = persistentFactory.loadContainerFrom(eachContent);
625 createWindowFor(eachId, container).show();