ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / ToolWindowsPane.java
blob57cbe1f43fb5f4977275f6b237243ad824bfa98d
1 /*
2 * Copyright 2000-2009 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.openapi.wm.impl;
18 import com.intellij.ide.ui.UISettings;
19 import com.intellij.ide.ui.UISettingsListener;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.ui.Splitter;
22 import com.intellij.openapi.ui.ThreeComponentsSplitter;
23 import com.intellij.openapi.util.SystemInfo;
24 import com.intellij.openapi.wm.ToolWindow;
25 import com.intellij.openapi.wm.ToolWindowAnchor;
26 import com.intellij.openapi.wm.ToolWindowType;
27 import com.intellij.openapi.wm.ex.ToolWindowEx;
28 import com.intellij.openapi.wm.impl.commands.FinalizableCommand;
29 import com.intellij.reference.SoftReference;
30 import com.intellij.util.containers.HashMap;
32 import javax.swing.*;
33 import java.awt.*;
34 import java.awt.event.ComponentEvent;
35 import java.awt.image.BufferedImage;
36 import java.util.ArrayList;
37 import java.util.Comparator;
39 /**
40 * This panel contains all tool stripes and JLayeredPanle at the center area. All tool windows are
41 * located inside this layered pane.
43 * @author Anton Katilin
44 * @author Vladimir Kondratyev
46 final class ToolWindowsPane extends JPanel{
47 private static final Logger LOG=Logger.getInstance("#com.intellij.openapi.wm.impl.ToolWindowsPane");
49 private final IdeFrameImpl myFrame;
51 private final HashMap<String,StripeButton> myId2Button;
52 private final HashMap<String,InternalDecorator> myId2Decorator;
53 private final HashMap<StripeButton, WindowInfoImpl> myButton2Info;
54 private final HashMap<InternalDecorator, WindowInfoImpl> myDecorator2Info;
55 /**
56 * This panel is the layered pane where all sliding tool windows are located. The DEFAULT
57 * layer contains splitters. The PALETTE layer contains all sliding tool windows.
59 private final MyLayeredPane myLayeredPane;
61 * Splitters.
63 private final ThreeComponentsSplitter myVerticalSplitter;
64 private final ThreeComponentsSplitter myHorizontalSplitter;
67 * Tool stripes.
69 private final Stripe myLeftStripe;
70 private final Stripe myRightStripe;
71 private final Stripe myBottomStripe;
72 private final Stripe myTopStripe;
74 private final ArrayList<Stripe> myStipes = new ArrayList<Stripe>();
76 private final MyUISettingsListenerImpl myUISettingsListener;
77 private final ToolWindowManagerImpl myManager;
79 ToolWindowsPane(final IdeFrameImpl frame, ToolWindowManagerImpl manager){
80 super(new BorderLayout());
82 myManager = manager;
84 setOpaque(false);
85 myFrame=frame;
86 myId2Button=new HashMap<String,StripeButton>();
87 myId2Decorator=new HashMap<String,InternalDecorator>();
88 myButton2Info=new HashMap<StripeButton, WindowInfoImpl>();
89 myDecorator2Info=new HashMap<InternalDecorator, WindowInfoImpl>();
90 myUISettingsListener=new MyUISettingsListenerImpl();
92 // Splitters
94 myVerticalSplitter = new ThreeComponentsSplitter(true);
95 myVerticalSplitter.setBackground(Color.gray);
96 myHorizontalSplitter = new ThreeComponentsSplitter(false);
97 myHorizontalSplitter.setBackground(Color.gray);
99 myVerticalSplitter.setInnerComponent(myHorizontalSplitter);
101 // Tool stripes
103 myTopStripe=new Stripe(SwingConstants.TOP, manager);
104 myStipes.add(myTopStripe);
105 myLeftStripe=new Stripe(SwingConstants.LEFT, manager);
106 myStipes.add(myLeftStripe);
107 myBottomStripe=new Stripe(SwingConstants.BOTTOM, manager);
108 myStipes.add(myBottomStripe);
109 myRightStripe=new Stripe(SwingConstants.RIGHT, manager);
110 myStipes.add(myRightStripe);
112 updateToolStripesVisibility();
114 // Layered pane
116 myLayeredPane=new MyLayeredPane(myVerticalSplitter);
118 // Compose layout
120 add(myTopStripe,BorderLayout.NORTH);
121 add(myLeftStripe,BorderLayout.WEST);
122 add(myBottomStripe,BorderLayout.SOUTH);
123 add(myRightStripe,BorderLayout.EAST);
124 add(myLayeredPane,BorderLayout.CENTER);
128 * Invoked when enclosed frame is being shown.
130 public final void addNotify(){
131 super.addNotify();
132 UISettings.getInstance().addUISettingsListener(myUISettingsListener);
136 * Invoked when enclosed frame is being disposed.
138 public final void removeNotify(){
139 UISettings.getInstance().removeUISettingsListener(myUISettingsListener);
140 super.removeNotify();
144 * Creates command which adds button into the specified tool stripe.
145 * Command uses copy of passed <code>info</code> object.
146 * @param button button which should be added.
147 * @param info window info for the corresponded tool window.
148 * @param comparator which is used to sort buttons within the stripe.
149 * @param finishCallBack invoked when the command is completed.
151 final FinalizableCommand createAddButtonCmd(final StripeButton button,final WindowInfoImpl info,final Comparator comparator,final Runnable finishCallBack){
152 final WindowInfoImpl copiedInfo=info.copy();
153 myId2Button.put(copiedInfo.getId(),button);
154 myButton2Info.put(button,copiedInfo);
155 return new AddToolStripeButtonCmd(button,copiedInfo,comparator,finishCallBack);
159 * Creates command which shows tool window with specified set of parameters.
160 * Command uses cloned copy of passed <code>info</code> object.
161 * @param dirtyMode if <code>true</code> then JRootPane will not be validated and repainted after adding
162 * the decorator. Moreover in this (dirty) mode animation doesn't work.
164 final FinalizableCommand createAddDecoratorCmd(
165 final InternalDecorator decorator,
166 final WindowInfoImpl info,
167 final boolean dirtyMode,
168 final Runnable finishCallBack
170 final WindowInfoImpl copiedInfo=info.copy();
171 final String id=copiedInfo.getId();
173 myDecorator2Info.put(decorator,copiedInfo);
174 myId2Decorator.put(id,decorator);
176 if(info.isDocked()){
177 WindowInfoImpl sideInfo = getDockedInfoAt(info.getAnchor(), !info.isSplit());
178 if (sideInfo == null) {
179 return new AddDockedComponentCmd(decorator,info,dirtyMode,finishCallBack);
181 else {
182 return new AddAndSplitDockedComponentCmd(decorator, info, dirtyMode, finishCallBack);
184 } else if(info.isSliding()) {
185 return new AddSlidingComponentCmd(decorator,info,dirtyMode,finishCallBack);
186 }else{
187 throw new IllegalArgumentException("Unknown window type: "+info.getType());
192 * Creates command which removes tool button from tool stripe.
193 * @param id <code>ID</code> of the button to be removed.
195 final FinalizableCommand createRemoveButtonCmd(final String id,final Runnable finishCallBack){
196 final StripeButton button=getButtonById(id);
197 final WindowInfoImpl info=getButtonInfoById(id);
199 myButton2Info.remove(button);
200 myId2Button.remove(id);
201 return new RemoveToolStripeButtonCmd(button,info,finishCallBack);
205 * Creates command which hides tool window with specified set of parameters.
206 * @param dirtyMode if <code>true</code> then JRootPane will not be validated and repainted after removing
207 * the decorator. Moreover in this (dirty) mode animation doesn't work.
209 final FinalizableCommand createRemoveDecoratorCmd(final String id, final boolean dirtyMode, final Runnable finishCallBack){
210 final Component decorator=getDecoratorById(id);
211 final WindowInfoImpl info=getDecoratorInfoById(id);
213 myDecorator2Info.remove(decorator);
214 myId2Decorator.remove(id);
216 WindowInfoImpl sideInfo = getDockedInfoAt(info.getAnchor(), !info.isSplit());
218 if(info.isDocked()){
219 if (sideInfo == null) {
220 return new RemoveDockedComponentCmd(info,dirtyMode,finishCallBack);
222 else {
223 return new RemoveSplitAndDockedComponentCmd(info, sideInfo, dirtyMode, finishCallBack);
225 }else if(info.isSliding()){
226 return new RemoveSlidingComponentCmd(decorator,info,dirtyMode,finishCallBack);
227 }else{
228 throw new IllegalArgumentException("Unknown window type");
233 * Creates command which sets specified document component.
234 * @param component component to be set.
236 final FinalizableCommand createSetEditorComponentCmd(final JComponent component,final Runnable finishCallBack){
237 return new SetEditorComponentCmd(component,finishCallBack);
240 final FinalizableCommand createUpdateButtonPositionCmd(String id, final Runnable finishCallback) {
241 return new UpdateButtonPositionCmd(id, finishCallback);
244 final JComponent getMyLayeredPane(){
245 return myLayeredPane;
248 private StripeButton getButtonById(final String id){
249 return myId2Button.get(id);
252 private Component getDecoratorById(final String id){
253 return myId2Decorator.get(id);
257 * @return <code>WindowInfo</code> associated with specified tool stripe button.
258 * @param id <code>ID</code> of tool stripe butoon.
260 private WindowInfoImpl getButtonInfoById(final String id){
261 return myButton2Info.get(myId2Button.get(id));
265 * @return <code>WindowInfo</code> associated with specified window decorator.
266 * @param id <code>ID</code> of decorator.
268 private WindowInfoImpl getDecoratorInfoById(final String id){
269 return myDecorator2Info.get(myId2Decorator.get(id));
273 * Sets (docks) specified component to the specified anchor.
275 private void setComponent(final JComponent component, final ToolWindowAnchor anchor, final float weight) {
276 if(ToolWindowAnchor.TOP==anchor){
277 myVerticalSplitter.setFirstComponent(component);
278 myVerticalSplitter.setFirstSize((int)(myLayeredPane.getHeight() * weight));
279 }else if(ToolWindowAnchor.LEFT==anchor){
280 myHorizontalSplitter.setFirstComponent(component);
281 myHorizontalSplitter.setFirstSize((int)(myLayeredPane.getWidth() * weight));
282 }else if(ToolWindowAnchor.BOTTOM==anchor){
283 myVerticalSplitter.setLastComponent(component);
284 myVerticalSplitter.setLastSize((int)(myLayeredPane.getHeight() * weight));
285 }else if(ToolWindowAnchor.RIGHT==anchor){
286 myHorizontalSplitter.setLastComponent(component);
287 myHorizontalSplitter.setLastSize((int)(myLayeredPane.getWidth() * weight));
288 }else{
289 LOG.error("unknown anchor: "+anchor);
293 private JComponent getComponentAt(ToolWindowAnchor anchor) {
294 if (ToolWindowAnchor.TOP == anchor) {
295 return myVerticalSplitter.getFirstComponent();
297 else if (ToolWindowAnchor.LEFT == anchor) {
298 return myHorizontalSplitter.getFirstComponent();
300 else if (ToolWindowAnchor.BOTTOM == anchor){
301 return myVerticalSplitter.getLastComponent();
303 else if (ToolWindowAnchor.RIGHT == anchor){
304 return myHorizontalSplitter.getLastComponent();
306 else {
307 LOG.error("unknown anchor: " + anchor);
308 return null;
312 private WindowInfoImpl getDockedInfoAt(ToolWindowAnchor anchor, boolean side) {
313 for (WindowInfoImpl info : myDecorator2Info.values()) {
314 if (info.isVisible() && info.isDocked() && info.getAnchor() == anchor && side == info.isSplit()) {
315 return info;
319 return null;
322 private void setDocumentComponent(final JComponent component){
323 myHorizontalSplitter.setInnerComponent(component);
326 private void updateToolStripesVisibility(){
327 final boolean visible = !UISettings.getInstance().HIDE_TOOL_STRIPES;
328 myLeftStripe.setVisible(visible);
329 myRightStripe.setVisible(visible);
330 myTopStripe.setVisible(visible);
331 myBottomStripe.setVisible(visible);
334 Stripe getStripeFor(String id) {
335 final ToolWindowAnchor anchor = myManager.getToolWindow(id).getAnchor();
336 if (ToolWindowAnchor.TOP == anchor) {
337 return myTopStripe;
338 } else if (ToolWindowAnchor.BOTTOM == anchor) {
339 return myBottomStripe;
340 } else if (ToolWindowAnchor.LEFT == anchor) {
341 return myLeftStripe;
342 } else if (ToolWindowAnchor.RIGHT == anchor) {
343 return myRightStripe;
346 throw new IllegalArgumentException("Anchor=" + anchor);
349 Stripe getStripeFor(final Rectangle screenRec, Stripe preferred) {
350 if (preferred.containsScreen(screenRec)) {
351 return myStipes.get(myStipes.indexOf(preferred));
354 for (Stripe each : myStipes) {
355 if (each.containsScreen(screenRec)) {
356 return myStipes.get(myStipes.indexOf(each));
360 return null;
363 void startDrag() {
364 for (Stripe each : myStipes) {
365 each.startDrag();
369 void stopDrag() {
370 for (Stripe each : myStipes) {
371 each.stopDrag();
375 public void stretchWidth(ToolWindow wnd, int value) {
376 stretch(wnd, value);
379 public void stretchHeight(ToolWindow wnd, int value) {
380 stretch(wnd, value);
383 private void stretch(ToolWindow wnd, int value) {
384 if (!wnd.isVisible()) return;
386 Resizer resizer = null;
387 Component cmp = null;
389 if (wnd.getType() == ToolWindowType.DOCKED) {
390 cmp = getComponentAt(wnd.getAnchor());
392 if (cmp != null) {
393 if (wnd.getAnchor().isHorizontal()) {
394 resizer = myVerticalSplitter.getFirstComponent() == cmp ? new Resizer.Splitter.FirstComponent(myVerticalSplitter) : new Resizer.Splitter.LastComponent(myVerticalSplitter);
395 } else {
396 resizer = myHorizontalSplitter.getFirstComponent() == cmp ? new Resizer.Splitter.FirstComponent(myHorizontalSplitter) : new Resizer.Splitter.LastComponent(myHorizontalSplitter);
399 } if (wnd.getType() == ToolWindowType.SLIDING) {
400 cmp = wnd.getComponent();
401 while (cmp != null) {
402 if (cmp.getParent() == myLayeredPane) break;
403 cmp = cmp.getParent();
406 if (cmp != null) {
407 if (wnd.getAnchor() == ToolWindowAnchor.TOP) {
408 resizer = new Resizer.LayeredPane.Top(cmp);
409 } else if (wnd.getAnchor() == ToolWindowAnchor.BOTTOM) {
410 resizer = new Resizer.LayeredPane.Bottom(cmp);
411 } else if (wnd.getAnchor() == ToolWindowAnchor.LEFT) {
412 resizer = new Resizer.LayeredPane.Left(cmp);
413 } else if (wnd.getAnchor() == ToolWindowAnchor.RIGHT) {
414 resizer = new Resizer.LayeredPane.Right(cmp);
419 if (resizer == null || cmp == null) return;
421 int currentValue = wnd.getAnchor().isHorizontal() ? cmp.getHeight() : cmp.getWidth();
423 int actualSize = currentValue + value;
425 int minValue = wnd.getAnchor().isHorizontal() ? ((ToolWindowEx)wnd).getDecorator().getTitlePanel().getPreferredSize().height : 16 + myHorizontalSplitter.getDividerWidth();
426 int maxValue = wnd.getAnchor().isHorizontal() ? myLayeredPane.getHeight() : myLayeredPane.getWidth();
429 if (actualSize < minValue) {
430 actualSize = minValue;
433 if (actualSize > maxValue) {
434 actualSize = maxValue;
437 resizer.setSize(actualSize);
441 static interface Resizer {
442 void setSize(int size);
445 abstract static class Splitter implements Resizer {
446 ThreeComponentsSplitter mySplitter;
448 Splitter(ThreeComponentsSplitter splitter) {
449 mySplitter = splitter;
452 static class FirstComponent extends Splitter {
453 FirstComponent(ThreeComponentsSplitter splitter) {
454 super(splitter);
457 public void setSize(int size) {
458 mySplitter.setFirstSize(size);
462 static class LastComponent extends Splitter {
463 LastComponent(ThreeComponentsSplitter splitter) {
464 super(splitter);
467 public void setSize(int size) {
468 mySplitter.setLastSize(size);
473 abstract static class LayeredPane implements Resizer {
474 Component myComponent;
476 protected LayeredPane(Component component) {
477 myComponent = component;
480 public final void setSize(int size) {
481 _setSize(size);
482 if (myComponent.getParent() instanceof JComponent) {
483 JComponent parent = (JComponent)myComponent;
484 parent.revalidate();
485 parent.repaint();
489 abstract void _setSize(int size);
491 static class Left extends LayeredPane {
493 Left(Component component) {
494 super(component);
497 public void _setSize(int size) {
498 myComponent.setSize(size, myComponent.getHeight());
502 static class Right extends LayeredPane {
503 Right(Component component) {
504 super(component);
507 public void _setSize(int size) {
508 Rectangle bounds = myComponent.getBounds();
509 int delta = size - bounds.width;
510 bounds.x -= delta;
511 bounds.width += delta;
512 myComponent.setBounds(bounds);
516 static class Top extends LayeredPane {
517 Top(Component component) {
518 super(component);
521 public void _setSize(int size) {
522 myComponent.setSize(myComponent.getWidth(), size);
526 static class Bottom extends LayeredPane {
527 Bottom(Component component) {
528 super(component);
531 public void _setSize(int size) {
532 Rectangle bounds = myComponent.getBounds();
533 int delta = size - bounds.height;
534 bounds.y -= delta;
535 bounds.height += delta;
536 myComponent.setBounds(bounds);
542 private final class AddDockedComponentCmd extends FinalizableCommand{
543 private final JComponent myComponent;
544 private final WindowInfoImpl myInfo;
545 private final boolean myDirtyMode;
547 public AddDockedComponentCmd(final JComponent component, final WindowInfoImpl info, final boolean dirtyMode, final Runnable finishCallBack){
548 super(finishCallBack);
549 myComponent=component;
550 myInfo=info;
551 myDirtyMode=dirtyMode;
554 public final void run(){
555 try{
556 float newWeight = myInfo.getWeight()<=.0f? WindowInfoImpl.DEFAULT_WEIGHT:myInfo.getWeight();
557 if(newWeight>=1.0f){
558 newWeight=1- WindowInfoImpl.DEFAULT_WEIGHT;
560 final ToolWindowAnchor anchor=myInfo.getAnchor();
561 setComponent(myComponent, anchor, newWeight);
562 if(!myDirtyMode){
563 myLayeredPane.validate();
564 myLayeredPane.repaint();
566 }finally{
567 finish();
572 private final class AddAndSplitDockedComponentCmd extends FinalizableCommand {
573 private final JComponent myNewComponent;
574 private final WindowInfoImpl myInfo;
575 private final boolean myDirtyMode;
577 private AddAndSplitDockedComponentCmd(final JComponent newComponent,
578 final WindowInfoImpl info, final boolean dirtyMode, final Runnable finishCallBack) {
579 super(finishCallBack);
580 myNewComponent = newComponent;
581 myInfo = info;
582 myDirtyMode = dirtyMode;
585 public void run() {
586 try {
587 float newWeight;
588 final ToolWindowAnchor anchor = myInfo.getAnchor();
589 Splitter splitter = new Splitter(!myInfo.getAnchor().isHorizontal());
590 InternalDecorator oldComponent = (InternalDecorator) getComponentAt(myInfo.getAnchor());
591 if (myInfo.isSplit()) {
592 splitter.setFirstComponent(oldComponent);
593 splitter.setSecondComponent(myNewComponent);
594 splitter.setProportion(normalizeWeigh(oldComponent.getWindowInfo().getSideWeight()));
595 newWeight = normalizeWeigh(oldComponent.getWindowInfo().getWeight());
597 else {
598 splitter.setFirstComponent(myNewComponent);
599 splitter.setSecondComponent(oldComponent);
600 splitter.setProportion(normalizeWeigh(myInfo.getSideWeight()));
601 newWeight = normalizeWeigh(myInfo.getWeight());
603 setComponent(splitter, anchor, newWeight);
605 if(!myDirtyMode){
606 myLayeredPane.validate();
607 myLayeredPane.repaint();
609 } finally {
610 finish();
614 private float normalizeWeigh(final float weight) {
615 float newWeight= weight <= .0f ? WindowInfoImpl.DEFAULT_WEIGHT : weight;
616 if (newWeight >= 1.0f) {
617 newWeight= 1- WindowInfoImpl.DEFAULT_WEIGHT;
619 return newWeight;
623 private final class AddSlidingComponentCmd extends FinalizableCommand{
624 private final Component myComponent;
625 private final WindowInfoImpl myInfo;
626 private final boolean myDirtyMode;
628 public AddSlidingComponentCmd(final Component component, final WindowInfoImpl info, final boolean dirtyMode, final Runnable finishCallBack){
629 super(finishCallBack);
630 myComponent=component;
631 myInfo=info;
632 myDirtyMode=dirtyMode;
635 public final void run(){
636 try{
637 // Show component.
638 final UISettings uiSettings=UISettings.getInstance();
639 if(!myDirtyMode && uiSettings.ANIMATE_WINDOWS && !UISettings.isRemoteDesktopConnected()){
640 // Prepare top image. This image is scrolling over bottom image.
641 final Image topImage=myLayeredPane.getTopImage();
642 final Graphics topGraphics=topImage.getGraphics();
644 Rectangle bounds;
646 try{
647 myLayeredPane.add(myComponent,JLayeredPane.PALETTE_LAYER);
648 myLayeredPane.moveToFront(myComponent);
649 myLayeredPane.setBoundsInPaletteLayer(myComponent,myInfo.getAnchor(),myInfo.getWeight());
650 bounds=myComponent.getBounds();
651 myComponent.paint(topGraphics);
652 myLayeredPane.remove(myComponent);
653 }finally{
654 topGraphics.dispose();
656 // Prepare bottom image.
657 final Image bottomImage=myLayeredPane.getBottomImage();
658 final Graphics bottomGraphics=bottomImage.getGraphics();
659 try{
660 bottomGraphics.setClip(0,0,bounds.width,bounds.height);
661 bottomGraphics.translate(-bounds.x,-bounds.y);
662 myLayeredPane.paint(bottomGraphics);
663 }finally{
664 bottomGraphics.dispose();
666 // Start animation.
667 final Surface surface=new Surface(topImage,bottomImage,1,myInfo.getAnchor(),uiSettings.ANIMATION_SPEED);
668 myLayeredPane.add(surface,JLayeredPane.PALETTE_LAYER);
669 surface.setBounds(bounds);
670 myLayeredPane.validate();
671 myLayeredPane.repaint();
673 surface.runMovement();
674 myLayeredPane.remove(surface);
675 myLayeredPane.add(myComponent,JLayeredPane.PALETTE_LAYER);
676 }else{ // not animated
677 myLayeredPane.add(myComponent,JLayeredPane.PALETTE_LAYER);
678 myLayeredPane.setBoundsInPaletteLayer(myComponent,myInfo.getAnchor(),myInfo.getWeight());
680 if(!myDirtyMode){
681 myLayeredPane.validate();
682 myLayeredPane.repaint();
684 }finally{
685 finish();
690 private final class AddToolStripeButtonCmd extends FinalizableCommand{
691 private final StripeButton myButton;
692 private final WindowInfoImpl myInfo;
693 private final Comparator myComparator;
695 public AddToolStripeButtonCmd(final StripeButton button,final WindowInfoImpl info,final Comparator comparator,final Runnable finishCallBack){
696 super(finishCallBack);
697 myButton=button;
698 myInfo=info;
699 myComparator=comparator;
702 public final void run(){
703 try{
704 final ToolWindowAnchor anchor=myInfo.getAnchor();
705 if(ToolWindowAnchor.TOP==anchor){
706 myTopStripe.addButton(myButton,myComparator);
707 }else if(ToolWindowAnchor.LEFT==anchor){
708 myLeftStripe.addButton(myButton,myComparator);
709 }else if(ToolWindowAnchor.BOTTOM==anchor){
710 myBottomStripe.addButton(myButton,myComparator);
711 }else if(ToolWindowAnchor.RIGHT==anchor){
712 myRightStripe.addButton(myButton,myComparator);
713 }else{
714 LOG.error("unknown anchor: "+anchor);
716 validate();
717 repaint();
718 }finally{
719 finish();
724 private final class RemoveToolStripeButtonCmd extends FinalizableCommand{
725 private final StripeButton myButton;
726 private final WindowInfoImpl myInfo;
728 public RemoveToolStripeButtonCmd(final StripeButton button,final WindowInfoImpl info,final Runnable finishCallBack){
729 super(finishCallBack);
730 myButton=button;
731 myInfo=info;
734 public final void run(){
735 try{
736 final ToolWindowAnchor anchor=myInfo.getAnchor();
737 if(ToolWindowAnchor.TOP==anchor){
738 myTopStripe.removeButton(myButton);
739 }else if(ToolWindowAnchor.LEFT==anchor){
740 myLeftStripe.removeButton(myButton);
741 }else if(ToolWindowAnchor.BOTTOM==anchor){
742 myBottomStripe.removeButton(myButton);
743 }else if(ToolWindowAnchor.RIGHT==anchor){
744 myRightStripe.removeButton(myButton);
745 }else{
746 LOG.error("unknown anchor: "+anchor);
748 validate();
749 repaint();
750 }finally{
751 finish();
756 private final class RemoveDockedComponentCmd extends FinalizableCommand{
757 private final WindowInfoImpl myInfo;
758 private final boolean myDirtyMode;
760 public RemoveDockedComponentCmd(final WindowInfoImpl info, final boolean dirtyMode, final Runnable finishCallBack){
761 super(finishCallBack);
762 myInfo=info;
763 myDirtyMode=dirtyMode;
766 public final void run(){
767 try{
768 setComponent(null,myInfo.getAnchor(), 0);
769 if(!myDirtyMode){
770 myLayeredPane.validate();
771 myLayeredPane.repaint();
773 }finally{
774 finish();
779 private final class RemoveSplitAndDockedComponentCmd extends FinalizableCommand {
780 private final WindowInfoImpl myInfo;
781 private final WindowInfoImpl mySideInfo;
782 private final boolean myDirtyMode;
784 private RemoveSplitAndDockedComponentCmd(final WindowInfoImpl info, final WindowInfoImpl sideInfo, boolean dirtyMode, final Runnable finishCallBack) {
785 super(finishCallBack);
786 myInfo = info;
787 mySideInfo = sideInfo;
788 myDirtyMode = dirtyMode;
791 public void run() {
792 try {
793 Splitter splitter = (Splitter) getComponentAt(myInfo.getAnchor());
795 if (myInfo.isSplit()) {
796 InternalDecorator component = (InternalDecorator)splitter.getFirstComponent();
797 setComponent(component, myInfo.getAnchor(), component.getWindowInfo().getWeight());
799 else {
800 InternalDecorator component = (InternalDecorator) splitter.getSecondComponent();
801 setComponent(component, myInfo.getAnchor(), component.getWindowInfo().getWeight());
803 if(!myDirtyMode){
804 myLayeredPane.validate();
805 myLayeredPane.repaint();
807 } finally {
808 finish();
814 private final class RemoveSlidingComponentCmd extends FinalizableCommand{
815 private final Component myComponent;
816 private final WindowInfoImpl myInfo;
817 private final boolean myDirtyMode;
819 public RemoveSlidingComponentCmd(
820 final Component component,
821 final WindowInfoImpl info,
822 final boolean dirtyMode,
823 final Runnable finishCallBack
825 super(finishCallBack);
826 myComponent=component;
827 myInfo=info;
828 myDirtyMode=dirtyMode;
831 public final void run(){
832 try{
833 final UISettings uiSettings=UISettings.getInstance();
834 if(!myDirtyMode && uiSettings.ANIMATE_WINDOWS && !UISettings.isRemoteDesktopConnected()){
835 final Rectangle bounds=myComponent.getBounds();
836 // Prepare top image. This image is scrolling over bottom image. It contains
837 // picture of component is being removed.
838 final Image topImage=myLayeredPane.getTopImage();
839 final Graphics topGraphics=topImage.getGraphics();
840 try{
841 myComponent.paint(topGraphics);
842 }finally{
843 topGraphics.dispose();
845 // Prepare bottom image. This image contains picture of component that is located
846 // under the component to is being removed.
847 final Image bottomImage=myLayeredPane.getBottomImage();
848 final Graphics bottomGraphics = bottomImage.getGraphics();
849 try{
850 myLayeredPane.remove(myComponent);
851 bottomGraphics.clipRect(0,0,bounds.width,bounds.height);
852 bottomGraphics.translate(-bounds.x,-bounds.y);
853 myLayeredPane.paint(bottomGraphics);
854 }finally{
855 bottomGraphics.dispose();
857 // Remove component from the layered pane and start animation.
858 final Surface surface=new Surface(topImage,bottomImage,-1,myInfo.getAnchor(),uiSettings.ANIMATION_SPEED * 2);
859 myLayeredPane.add(surface,JLayeredPane.PALETTE_LAYER);
860 surface.setBounds(bounds);
861 myLayeredPane.validate();
862 myLayeredPane.repaint();
864 surface.runMovement();
865 myLayeredPane.remove(surface);
866 }else{ // not animated
867 myLayeredPane.remove(myComponent);
869 if(!myDirtyMode){
870 myLayeredPane.validate();
871 myLayeredPane.repaint();
873 }finally{
874 finish();
879 private final class SetEditorComponentCmd extends FinalizableCommand{
880 private final JComponent myComponent;
882 public SetEditorComponentCmd(final JComponent component,final Runnable finishCallBack){
883 super(finishCallBack);
884 myComponent=component;
887 public void run(){
888 try{
889 setDocumentComponent(myComponent);
890 myLayeredPane.validate();
891 myLayeredPane.repaint();
892 }finally{
893 finish();
898 private final class UpdateButtonPositionCmd extends FinalizableCommand {
899 private final String myId;
901 private UpdateButtonPositionCmd(String id, final Runnable finishCallBack) {
902 super(finishCallBack);
903 myId = id;
907 public void run() {
908 try {
909 WindowInfoImpl info = getButtonById(myId).getWindowInfo();
910 ToolWindowAnchor anchor = info.getAnchor();
912 if (ToolWindowAnchor.TOP == anchor){
913 myTopStripe.revalidate();
915 else if (ToolWindowAnchor.LEFT == anchor){
916 myLeftStripe.revalidate();
918 else if (ToolWindowAnchor.BOTTOM == anchor) {
919 myBottomStripe.revalidate();
921 else if (ToolWindowAnchor.RIGHT == anchor) {
922 myRightStripe.revalidate();
924 else {
925 LOG.error("unknown anchor: " + anchor);
927 } finally {
928 finish();
933 private final class MyUISettingsListenerImpl implements UISettingsListener{
934 public final void uiSettingsChanged(final UISettings source){
935 updateToolStripesVisibility();
939 private final class MyLayeredPane extends JLayeredPane{
941 * These images are used to perform animated showing and hiding of components.
942 * They are the member for performance reason.
944 private SoftReference myBottomImageRef;
945 private SoftReference myTopImageRef;
947 public MyLayeredPane(final JComponent splitter) {
948 myBottomImageRef=new SoftReference(null);
949 myTopImageRef=new SoftReference(null);
950 setOpaque(true);
951 setBackground(Color.gray);
952 add(splitter,JLayeredPane.DEFAULT_LAYER);
953 splitter.setBounds(0,0,getWidth(),getHeight());
954 enableEvents(ComponentEvent.COMPONENT_EVENT_MASK);
958 * TODO[vova] extract method
959 * Lazily creates and returns bottom image for animation.
961 public final Image getBottomImage(){
962 LOG.assertTrue(UISettings.getInstance().ANIMATE_WINDOWS);
963 BufferedImage image=(BufferedImage)myBottomImageRef.get();
965 image==null ||
966 image.getWidth(null) < getWidth() || image.getHeight(null) < getHeight()
968 final int width=Math.max(Math.max(1,getWidth()),myFrame.getWidth());
969 final int height=Math.max(Math.max(1,getHeight()),myFrame.getHeight());
970 if(SystemInfo.isWindows || SystemInfo.isMac){
971 image=myFrame.getGraphicsConfiguration().createCompatibleImage(width,height);
972 }else{
973 // Under Linux we have found that images created by createCompatibleImage(),
974 // createVolatileImage(), etc extremely slow for rendering. TrueColor buffered image
975 // is MUCH faster.
976 image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
978 myBottomImageRef=new SoftReference(image);
980 return image;
984 * TODO[vova] extract method
985 * Lazily creates and returns top image for animation.
987 public final Image getTopImage(){
988 LOG.assertTrue(UISettings.getInstance().ANIMATE_WINDOWS);
989 BufferedImage image=(BufferedImage)myTopImageRef.get();
991 image==null ||
992 image.getWidth(null) < getWidth() || image.getHeight(null) < getHeight()
994 final int width=Math.max(Math.max(1,getWidth()),myFrame.getWidth());
995 final int height=Math.max(Math.max(1,getHeight()),myFrame.getHeight());
996 if(SystemInfo.isWindows || SystemInfo.isMac){
997 image=myFrame.getGraphicsConfiguration().createCompatibleImage(width,height);
998 }else{
999 // Under Linux we have found that images created by createCompatibleImage(),
1000 // createVolatileImage(), etc extremely slow for rendering. TrueColor buffered image
1001 // is MUCH faster.
1002 image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
1004 myTopImageRef=new SoftReference(image);
1006 return image;
1010 * When component size becomes larger then bottom and top images should be enlarged.
1012 protected final void processComponentEvent(final ComponentEvent e) {
1013 if(ComponentEvent.COMPONENT_RESIZED==e.getID()){
1014 final int width=getWidth();
1015 final int height=getHeight();
1016 if(width<0||height<0){
1017 return;
1019 // Resize component at the DEFAULT layer. It should be only on component in that layer
1020 Component[] components=getComponentsInLayer(JLayeredPane.DEFAULT_LAYER.intValue());
1021 LOG.assertTrue(components.length<=1);
1022 for(int i=0;i<components.length;i++){
1023 final Component component=components[i];
1024 component.setBounds(0,0,getWidth(),getHeight());
1026 // Resize components at the PALETTE layer
1027 components=getComponentsInLayer(JLayeredPane.PALETTE_LAYER.intValue());
1028 for(int i=0;i<components.length;i++){
1029 final Component component=components[i];
1030 if (!(component instanceof InternalDecorator)) {
1031 continue;
1033 final WindowInfoImpl info=myDecorator2Info.get(component);
1034 // In normal situation info is not null. But sometimes Swing sends resize
1035 // event to removed component. See SCR #19566.
1036 if(info == null){
1037 continue;
1040 final float weight;
1041 if(ToolWindowAnchor.TOP==info.getAnchor()||ToolWindowAnchor.BOTTOM==info.getAnchor()){
1042 weight=(float)component.getHeight()/(float)getHeight();
1043 }else{
1044 weight=(float)component.getWidth()/(float)getWidth();
1046 setBoundsInPaletteLayer(component,info.getAnchor(),weight);
1048 validate();
1049 repaint();
1050 }else{
1051 super.processComponentEvent(e);
1055 public final void setBoundsInPaletteLayer(final Component component,final ToolWindowAnchor anchor,float weight){
1056 if(weight<.0f){
1057 weight= WindowInfoImpl.DEFAULT_WEIGHT;
1058 }else if(weight>1.0f){
1059 weight=1.0f;
1061 if(ToolWindowAnchor.TOP==anchor){
1062 component.setBounds(0,0,getWidth(),(int)(getHeight()*weight+.5f));
1063 }else if(ToolWindowAnchor.LEFT==anchor){
1064 component.setBounds(0,0,(int)(getWidth()*weight+.5f),getHeight());
1065 }else if(ToolWindowAnchor.BOTTOM==anchor){
1066 final int height=(int)(getHeight()*weight+.5f);
1067 component.setBounds(0,getHeight()-height,getWidth(),height);
1068 }else if(ToolWindowAnchor.RIGHT==anchor){
1069 final int width=(int)(getWidth()*weight+.5f);
1070 component.setBounds(getWidth()-width,0,width,getHeight());
1071 }else{
1072 LOG.error("unknown anchor "+anchor);
1077 //todo
1078 static int getHorizontalInsetX() {
1079 return 19;