toolwindow resize for docked windows
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / ToolWindowsPane.java
blobae9fff4c562925a9cafd081a331d994dc5405c9e
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.impl.commands.FinalizableCommand;
27 import com.intellij.reference.SoftReference;
28 import com.intellij.util.containers.HashMap;
30 import javax.swing.*;
31 import java.awt.*;
32 import java.awt.event.ComponentEvent;
33 import java.awt.image.BufferedImage;
34 import java.util.ArrayList;
35 import java.util.Comparator;
37 /**
38 * This panel contains all tool stripes and JLayeredPanle at the center area. All tool windows are
39 * located inside this layered pane.
41 * @author Anton Katilin
42 * @author Vladimir Kondratyev
44 final class ToolWindowsPane extends JPanel{
45 private static final Logger LOG=Logger.getInstance("#com.intellij.openapi.wm.impl.ToolWindowsPane");
47 private final IdeFrameImpl myFrame;
49 private final HashMap<String,StripeButton> myId2Button;
50 private final HashMap<String,InternalDecorator> myId2Decorator;
51 private final HashMap<StripeButton, WindowInfoImpl> myButton2Info;
52 private final HashMap<InternalDecorator, WindowInfoImpl> myDecorator2Info;
53 /**
54 * This panel is the layered pane where all sliding tool windows are located. The DEFAULT
55 * layer contains splitters. The PALETTE layer contains all sliding tool windows.
57 private final MyLayeredPane myLayeredPane;
59 * Splitters.
61 private final ThreeComponentsSplitter myVerticalSplitter;
62 private final ThreeComponentsSplitter myHorizontalSplitter;
65 * Tool stripes.
67 private final Stripe myLeftStripe;
68 private final Stripe myRightStripe;
69 private final Stripe myBottomStripe;
70 private final Stripe myTopStripe;
72 private final ArrayList<Stripe> myStipes = new ArrayList<Stripe>();
74 private final MyUISettingsListenerImpl myUISettingsListener;
75 private final ToolWindowManagerImpl myManager;
77 ToolWindowsPane(final IdeFrameImpl frame, ToolWindowManagerImpl manager){
78 super(new BorderLayout());
80 myManager = manager;
82 setOpaque(false);
83 myFrame=frame;
84 myId2Button=new HashMap<String,StripeButton>();
85 myId2Decorator=new HashMap<String,InternalDecorator>();
86 myButton2Info=new HashMap<StripeButton, WindowInfoImpl>();
87 myDecorator2Info=new HashMap<InternalDecorator, WindowInfoImpl>();
88 myUISettingsListener=new MyUISettingsListenerImpl();
90 // Splitters
92 myVerticalSplitter = new ThreeComponentsSplitter(true);
93 myVerticalSplitter.setBackground(Color.gray);
94 myHorizontalSplitter = new ThreeComponentsSplitter(false);
95 myHorizontalSplitter.setBackground(Color.gray);
97 myVerticalSplitter.setInnerComponent(myHorizontalSplitter);
99 // Tool stripes
101 myTopStripe=new Stripe(SwingConstants.TOP, manager);
102 myStipes.add(myTopStripe);
103 myLeftStripe=new Stripe(SwingConstants.LEFT, manager);
104 myStipes.add(myLeftStripe);
105 myBottomStripe=new Stripe(SwingConstants.BOTTOM, manager);
106 myStipes.add(myBottomStripe);
107 myRightStripe=new Stripe(SwingConstants.RIGHT, manager);
108 myStipes.add(myRightStripe);
110 updateToolStripesVisibility();
112 // Layered pane
114 myLayeredPane=new MyLayeredPane(myVerticalSplitter);
116 // Compose layout
118 add(myTopStripe,BorderLayout.NORTH);
119 add(myLeftStripe,BorderLayout.WEST);
120 add(myBottomStripe,BorderLayout.SOUTH);
121 add(myRightStripe,BorderLayout.EAST);
122 add(myLayeredPane,BorderLayout.CENTER);
126 * Invoked when enclosed frame is being shown.
128 public final void addNotify(){
129 super.addNotify();
130 UISettings.getInstance().addUISettingsListener(myUISettingsListener);
134 * Invoked when enclosed frame is being disposed.
136 public final void removeNotify(){
137 UISettings.getInstance().removeUISettingsListener(myUISettingsListener);
138 super.removeNotify();
142 * Creates command which adds button into the specified tool stripe.
143 * Command uses copy of passed <code>info</code> object.
144 * @param button button which should be added.
145 * @param info window info for the corresponded tool window.
146 * @param comparator which is used to sort buttons within the stripe.
147 * @param finishCallBack invoked when the command is completed.
149 final FinalizableCommand createAddButtonCmd(final StripeButton button,final WindowInfoImpl info,final Comparator comparator,final Runnable finishCallBack){
150 final WindowInfoImpl copiedInfo=info.copy();
151 myId2Button.put(copiedInfo.getId(),button);
152 myButton2Info.put(button,copiedInfo);
153 return new AddToolStripeButtonCmd(button,copiedInfo,comparator,finishCallBack);
157 * Creates command which shows tool window with specified set of parameters.
158 * Command uses cloned copy of passed <code>info</code> object.
159 * @param dirtyMode if <code>true</code> then JRootPane will not be validated and repainted after adding
160 * the decorator. Moreover in this (dirty) mode animation doesn't work.
162 final FinalizableCommand createAddDecoratorCmd(
163 final InternalDecorator decorator,
164 final WindowInfoImpl info,
165 final boolean dirtyMode,
166 final Runnable finishCallBack
168 final WindowInfoImpl copiedInfo=info.copy();
169 final String id=copiedInfo.getId();
171 myDecorator2Info.put(decorator,copiedInfo);
172 myId2Decorator.put(id,decorator);
174 if(info.isDocked()){
175 WindowInfoImpl sideInfo = getDockedInfoAt(info.getAnchor(), !info.isSplit());
176 if (sideInfo == null) {
177 return new AddDockedComponentCmd(decorator,info,dirtyMode,finishCallBack);
179 else {
180 return new AddAndSplitDockedComponentCmd(decorator, info, dirtyMode, finishCallBack);
182 } else if(info.isSliding()) {
183 return new AddSlidingComponentCmd(decorator,info,dirtyMode,finishCallBack);
184 }else{
185 throw new IllegalArgumentException("Unknown window type: "+info.getType());
190 * Creates command which removes tool button from tool stripe.
191 * @param id <code>ID</code> of the button to be removed.
193 final FinalizableCommand createRemoveButtonCmd(final String id,final Runnable finishCallBack){
194 final StripeButton button=getButtonById(id);
195 final WindowInfoImpl info=getButtonInfoById(id);
197 myButton2Info.remove(button);
198 myId2Button.remove(id);
199 return new RemoveToolStripeButtonCmd(button,info,finishCallBack);
203 * Creates command which hides tool window with specified set of parameters.
204 * @param dirtyMode if <code>true</code> then JRootPane will not be validated and repainted after removing
205 * the decorator. Moreover in this (dirty) mode animation doesn't work.
207 final FinalizableCommand createRemoveDecoratorCmd(final String id, final boolean dirtyMode, final Runnable finishCallBack){
208 final Component decorator=getDecoratorById(id);
209 final WindowInfoImpl info=getDecoratorInfoById(id);
211 myDecorator2Info.remove(decorator);
212 myId2Decorator.remove(id);
214 WindowInfoImpl sideInfo = getDockedInfoAt(info.getAnchor(), !info.isSplit());
216 if(info.isDocked()){
217 if (sideInfo == null) {
218 return new RemoveDockedComponentCmd(info,dirtyMode,finishCallBack);
220 else {
221 return new RemoveSplitAndDockedComponentCmd(info, sideInfo, dirtyMode, finishCallBack);
223 }else if(info.isSliding()){
224 return new RemoveSlidingComponentCmd(decorator,info,dirtyMode,finishCallBack);
225 }else{
226 throw new IllegalArgumentException("Unknown window type");
231 * Creates command which sets specified document component.
232 * @param component component to be set.
234 final FinalizableCommand createSetEditorComponentCmd(final JComponent component,final Runnable finishCallBack){
235 return new SetEditorComponentCmd(component,finishCallBack);
238 final FinalizableCommand createUpdateButtonPositionCmd(String id, final Runnable finishCallback) {
239 return new UpdateButtonPositionCmd(id, finishCallback);
242 final JComponent getMyLayeredPane(){
243 return myLayeredPane;
246 private StripeButton getButtonById(final String id){
247 return myId2Button.get(id);
250 private Component getDecoratorById(final String id){
251 return myId2Decorator.get(id);
255 * @return <code>WindowInfo</code> associated with specified tool stripe button.
256 * @param id <code>ID</code> of tool stripe butoon.
258 private WindowInfoImpl getButtonInfoById(final String id){
259 return myButton2Info.get(myId2Button.get(id));
263 * @return <code>WindowInfo</code> associated with specified window decorator.
264 * @param id <code>ID</code> of decorator.
266 private WindowInfoImpl getDecoratorInfoById(final String id){
267 return myDecorator2Info.get(myId2Decorator.get(id));
271 * Sets (docks) specified component to the specified anchor.
273 private void setComponent(final JComponent component, final ToolWindowAnchor anchor, final float weight) {
274 if(ToolWindowAnchor.TOP==anchor){
275 myVerticalSplitter.setFirstComponent(component);
276 myVerticalSplitter.setFirstSize((int)(myLayeredPane.getHeight() * weight));
277 }else if(ToolWindowAnchor.LEFT==anchor){
278 myHorizontalSplitter.setFirstComponent(component);
279 myHorizontalSplitter.setFirstSize((int)(myLayeredPane.getWidth() * weight));
280 }else if(ToolWindowAnchor.BOTTOM==anchor){
281 myVerticalSplitter.setLastComponent(component);
282 myVerticalSplitter.setLastSize((int)(myLayeredPane.getHeight() * weight));
283 }else if(ToolWindowAnchor.RIGHT==anchor){
284 myHorizontalSplitter.setLastComponent(component);
285 myHorizontalSplitter.setLastSize((int)(myLayeredPane.getWidth() * weight));
286 }else{
287 LOG.error("unknown anchor: "+anchor);
291 private JComponent getComponentAt(ToolWindowAnchor anchor) {
292 if (ToolWindowAnchor.TOP == anchor) {
293 return myVerticalSplitter.getFirstComponent();
295 else if (ToolWindowAnchor.LEFT == anchor) {
296 return myHorizontalSplitter.getFirstComponent();
298 else if (ToolWindowAnchor.BOTTOM == anchor){
299 return myVerticalSplitter.getLastComponent();
301 else if (ToolWindowAnchor.RIGHT == anchor){
302 return myHorizontalSplitter.getLastComponent();
304 else {
305 LOG.error("unknown anchor: " + anchor);
306 return null;
310 private WindowInfoImpl getDockedInfoAt(ToolWindowAnchor anchor, boolean side) {
311 for (WindowInfoImpl info : myDecorator2Info.values()) {
312 if (info.isVisible() && info.isDocked() && info.getAnchor() == anchor && side == info.isSplit()) {
313 return info;
317 return null;
320 private void setDocumentComponent(final JComponent component){
321 myHorizontalSplitter.setInnerComponent(component);
324 private void updateToolStripesVisibility(){
325 final boolean visible = !UISettings.getInstance().HIDE_TOOL_STRIPES;
326 myLeftStripe.setVisible(visible);
327 myRightStripe.setVisible(visible);
328 myTopStripe.setVisible(visible);
329 myBottomStripe.setVisible(visible);
332 Stripe getStripeFor(String id) {
333 final ToolWindowAnchor anchor = myManager.getToolWindow(id).getAnchor();
334 if (ToolWindowAnchor.TOP == anchor) {
335 return myTopStripe;
336 } else if (ToolWindowAnchor.BOTTOM == anchor) {
337 return myBottomStripe;
338 } else if (ToolWindowAnchor.LEFT == anchor) {
339 return myLeftStripe;
340 } else if (ToolWindowAnchor.RIGHT == anchor) {
341 return myRightStripe;
344 throw new IllegalArgumentException("Anchor=" + anchor);
347 Stripe getStripeFor(final Rectangle screenRec, Stripe preferred) {
348 if (preferred.containsScreen(screenRec)) {
349 return myStipes.get(myStipes.indexOf(preferred));
352 for (Stripe each : myStipes) {
353 if (each.containsScreen(screenRec)) {
354 return myStipes.get(myStipes.indexOf(each));
358 return null;
361 void startDrag() {
362 for (Stripe each : myStipes) {
363 each.startDrag();
367 void stopDrag() {
368 for (Stripe each : myStipes) {
369 each.stopDrag();
373 public void stretchWidth(ToolWindow wnd, int value) {
374 JComponent cmp = getComponentAt(wnd.getAnchor());
375 if (cmp == null) return;
377 stretch(myHorizontalSplitter, cmp, cmp.getSize().width, value, 0, getSize().width);
380 public void stretchHeight(ToolWindow wnd, int value) {
381 JComponent cmp = getComponentAt(wnd.getAnchor());
382 if (cmp == null) return;
384 stretch(myVerticalSplitter, cmp, cmp.getSize().height, value, 0, getSize().height);
387 private void stretch(ThreeComponentsSplitter splitter, JComponent cmp, int currentValue, int incValue, int minValue, int maxValue) {
388 int actualSize = currentValue + incValue;
390 if (actualSize < minValue) {
391 actualSize = minValue;
394 if (actualSize > maxValue) {
395 actualSize = maxValue;
398 if (splitter.getFirstComponent() == cmp) {
399 splitter.setFirstSize(actualSize);
400 } else if (splitter.getLastComponent() == cmp) {
401 splitter.setLastSize(actualSize);
405 private final class AddDockedComponentCmd extends FinalizableCommand{
406 private final JComponent myComponent;
407 private final WindowInfoImpl myInfo;
408 private final boolean myDirtyMode;
410 public AddDockedComponentCmd(final JComponent component, final WindowInfoImpl info, final boolean dirtyMode, final Runnable finishCallBack){
411 super(finishCallBack);
412 myComponent=component;
413 myInfo=info;
414 myDirtyMode=dirtyMode;
417 public final void run(){
418 try{
419 float newWeight = myInfo.getWeight()<=.0f? WindowInfoImpl.DEFAULT_WEIGHT:myInfo.getWeight();
420 if(newWeight>=1.0f){
421 newWeight=1- WindowInfoImpl.DEFAULT_WEIGHT;
423 final ToolWindowAnchor anchor=myInfo.getAnchor();
424 setComponent(myComponent, anchor, newWeight);
425 if(!myDirtyMode){
426 myLayeredPane.validate();
427 myLayeredPane.repaint();
429 }finally{
430 finish();
435 private final class AddAndSplitDockedComponentCmd extends FinalizableCommand {
436 private final JComponent myNewComponent;
437 private final WindowInfoImpl myInfo;
438 private final boolean myDirtyMode;
440 private AddAndSplitDockedComponentCmd(final JComponent newComponent,
441 final WindowInfoImpl info, final boolean dirtyMode, final Runnable finishCallBack) {
442 super(finishCallBack);
443 myNewComponent = newComponent;
444 myInfo = info;
445 myDirtyMode = dirtyMode;
448 public void run() {
449 try {
450 float newWeight;
451 final ToolWindowAnchor anchor = myInfo.getAnchor();
452 Splitter splitter = new Splitter(!myInfo.getAnchor().isHorizontal());
453 InternalDecorator oldComponent = (InternalDecorator) getComponentAt(myInfo.getAnchor());
454 if (myInfo.isSplit()) {
455 splitter.setFirstComponent(oldComponent);
456 splitter.setSecondComponent(myNewComponent);
457 splitter.setProportion(normalizeWeigh(oldComponent.getWindowInfo().getSideWeight()));
458 newWeight = normalizeWeigh(oldComponent.getWindowInfo().getWeight());
460 else {
461 splitter.setFirstComponent(myNewComponent);
462 splitter.setSecondComponent(oldComponent);
463 splitter.setProportion(normalizeWeigh(myInfo.getSideWeight()));
464 newWeight = normalizeWeigh(myInfo.getWeight());
466 setComponent(splitter, anchor, newWeight);
468 if(!myDirtyMode){
469 myLayeredPane.validate();
470 myLayeredPane.repaint();
472 } finally {
473 finish();
477 private float normalizeWeigh(final float weight) {
478 float newWeight= weight <= .0f ? WindowInfoImpl.DEFAULT_WEIGHT : weight;
479 if (newWeight >= 1.0f) {
480 newWeight= 1- WindowInfoImpl.DEFAULT_WEIGHT;
482 return newWeight;
486 private final class AddSlidingComponentCmd extends FinalizableCommand{
487 private final Component myComponent;
488 private final WindowInfoImpl myInfo;
489 private final boolean myDirtyMode;
491 public AddSlidingComponentCmd(final Component component, final WindowInfoImpl info, final boolean dirtyMode, final Runnable finishCallBack){
492 super(finishCallBack);
493 myComponent=component;
494 myInfo=info;
495 myDirtyMode=dirtyMode;
498 public final void run(){
499 try{
500 // Show component.
501 final UISettings uiSettings=UISettings.getInstance();
502 if(!myDirtyMode && uiSettings.ANIMATE_WINDOWS && !UISettings.isRemoteDesktopConnected()){
503 // Prepare top image. This image is scrolling over bottom image.
504 final Image topImage=myLayeredPane.getTopImage();
505 final Graphics topGraphics=topImage.getGraphics();
507 Rectangle bounds;
509 try{
510 myLayeredPane.add(myComponent,JLayeredPane.PALETTE_LAYER);
511 myLayeredPane.moveToFront(myComponent);
512 myLayeredPane.setBoundsInPaletteLayer(myComponent,myInfo.getAnchor(),myInfo.getWeight());
513 bounds=myComponent.getBounds();
514 myComponent.paint(topGraphics);
515 myLayeredPane.remove(myComponent);
516 }finally{
517 topGraphics.dispose();
519 // Prepare bottom image.
520 final Image bottomImage=myLayeredPane.getBottomImage();
521 final Graphics bottomGraphics=bottomImage.getGraphics();
522 try{
523 bottomGraphics.setClip(0,0,bounds.width,bounds.height);
524 bottomGraphics.translate(-bounds.x,-bounds.y);
525 myLayeredPane.paint(bottomGraphics);
526 }finally{
527 bottomGraphics.dispose();
529 // Start animation.
530 final Surface surface=new Surface(topImage,bottomImage,1,myInfo.getAnchor(),uiSettings.ANIMATION_SPEED);
531 myLayeredPane.add(surface,JLayeredPane.PALETTE_LAYER);
532 surface.setBounds(bounds);
533 myLayeredPane.validate();
534 myLayeredPane.repaint();
536 surface.runMovement();
537 myLayeredPane.remove(surface);
538 myLayeredPane.add(myComponent,JLayeredPane.PALETTE_LAYER);
539 }else{ // not animated
540 myLayeredPane.add(myComponent,JLayeredPane.PALETTE_LAYER);
541 myLayeredPane.setBoundsInPaletteLayer(myComponent,myInfo.getAnchor(),myInfo.getWeight());
543 if(!myDirtyMode){
544 myLayeredPane.validate();
545 myLayeredPane.repaint();
547 }finally{
548 finish();
553 private final class AddToolStripeButtonCmd extends FinalizableCommand{
554 private final StripeButton myButton;
555 private final WindowInfoImpl myInfo;
556 private final Comparator myComparator;
558 public AddToolStripeButtonCmd(final StripeButton button,final WindowInfoImpl info,final Comparator comparator,final Runnable finishCallBack){
559 super(finishCallBack);
560 myButton=button;
561 myInfo=info;
562 myComparator=comparator;
565 public final void run(){
566 try{
567 final ToolWindowAnchor anchor=myInfo.getAnchor();
568 if(ToolWindowAnchor.TOP==anchor){
569 myTopStripe.addButton(myButton,myComparator);
570 }else if(ToolWindowAnchor.LEFT==anchor){
571 myLeftStripe.addButton(myButton,myComparator);
572 }else if(ToolWindowAnchor.BOTTOM==anchor){
573 myBottomStripe.addButton(myButton,myComparator);
574 }else if(ToolWindowAnchor.RIGHT==anchor){
575 myRightStripe.addButton(myButton,myComparator);
576 }else{
577 LOG.error("unknown anchor: "+anchor);
579 validate();
580 repaint();
581 }finally{
582 finish();
587 private final class RemoveToolStripeButtonCmd extends FinalizableCommand{
588 private final StripeButton myButton;
589 private final WindowInfoImpl myInfo;
591 public RemoveToolStripeButtonCmd(final StripeButton button,final WindowInfoImpl info,final Runnable finishCallBack){
592 super(finishCallBack);
593 myButton=button;
594 myInfo=info;
597 public final void run(){
598 try{
599 final ToolWindowAnchor anchor=myInfo.getAnchor();
600 if(ToolWindowAnchor.TOP==anchor){
601 myTopStripe.removeButton(myButton);
602 }else if(ToolWindowAnchor.LEFT==anchor){
603 myLeftStripe.removeButton(myButton);
604 }else if(ToolWindowAnchor.BOTTOM==anchor){
605 myBottomStripe.removeButton(myButton);
606 }else if(ToolWindowAnchor.RIGHT==anchor){
607 myRightStripe.removeButton(myButton);
608 }else{
609 LOG.error("unknown anchor: "+anchor);
611 validate();
612 repaint();
613 }finally{
614 finish();
619 private final class RemoveDockedComponentCmd extends FinalizableCommand{
620 private final WindowInfoImpl myInfo;
621 private final boolean myDirtyMode;
623 public RemoveDockedComponentCmd(final WindowInfoImpl info, final boolean dirtyMode, final Runnable finishCallBack){
624 super(finishCallBack);
625 myInfo=info;
626 myDirtyMode=dirtyMode;
629 public final void run(){
630 try{
631 setComponent(null,myInfo.getAnchor(), 0);
632 if(!myDirtyMode){
633 myLayeredPane.validate();
634 myLayeredPane.repaint();
636 }finally{
637 finish();
642 private final class RemoveSplitAndDockedComponentCmd extends FinalizableCommand {
643 private final WindowInfoImpl myInfo;
644 private final WindowInfoImpl mySideInfo;
645 private final boolean myDirtyMode;
647 private RemoveSplitAndDockedComponentCmd(final WindowInfoImpl info, final WindowInfoImpl sideInfo, boolean dirtyMode, final Runnable finishCallBack) {
648 super(finishCallBack);
649 myInfo = info;
650 mySideInfo = sideInfo;
651 myDirtyMode = dirtyMode;
654 public void run() {
655 try {
656 Splitter splitter = (Splitter) getComponentAt(myInfo.getAnchor());
658 if (myInfo.isSplit()) {
659 InternalDecorator component = (InternalDecorator)splitter.getFirstComponent();
660 setComponent(component, myInfo.getAnchor(), component.getWindowInfo().getWeight());
662 else {
663 InternalDecorator component = (InternalDecorator) splitter.getSecondComponent();
664 setComponent(component, myInfo.getAnchor(), component.getWindowInfo().getWeight());
666 if(!myDirtyMode){
667 myLayeredPane.validate();
668 myLayeredPane.repaint();
670 } finally {
671 finish();
677 private final class RemoveSlidingComponentCmd extends FinalizableCommand{
678 private final Component myComponent;
679 private final WindowInfoImpl myInfo;
680 private final boolean myDirtyMode;
682 public RemoveSlidingComponentCmd(
683 final Component component,
684 final WindowInfoImpl info,
685 final boolean dirtyMode,
686 final Runnable finishCallBack
688 super(finishCallBack);
689 myComponent=component;
690 myInfo=info;
691 myDirtyMode=dirtyMode;
694 public final void run(){
695 try{
696 final UISettings uiSettings=UISettings.getInstance();
697 if(!myDirtyMode && uiSettings.ANIMATE_WINDOWS && !UISettings.isRemoteDesktopConnected()){
698 final Rectangle bounds=myComponent.getBounds();
699 // Prepare top image. This image is scrolling over bottom image. It contains
700 // picture of component is being removed.
701 final Image topImage=myLayeredPane.getTopImage();
702 final Graphics topGraphics=topImage.getGraphics();
703 try{
704 myComponent.paint(topGraphics);
705 }finally{
706 topGraphics.dispose();
708 // Prepare bottom image. This image contains picture of component that is located
709 // under the component to is being removed.
710 final Image bottomImage=myLayeredPane.getBottomImage();
711 final Graphics bottomGraphics = bottomImage.getGraphics();
712 try{
713 myLayeredPane.remove(myComponent);
714 bottomGraphics.clipRect(0,0,bounds.width,bounds.height);
715 bottomGraphics.translate(-bounds.x,-bounds.y);
716 myLayeredPane.paint(bottomGraphics);
717 }finally{
718 bottomGraphics.dispose();
720 // Remove component from the layered pane and start animation.
721 final Surface surface=new Surface(topImage,bottomImage,-1,myInfo.getAnchor(),uiSettings.ANIMATION_SPEED * 2);
722 myLayeredPane.add(surface,JLayeredPane.PALETTE_LAYER);
723 surface.setBounds(bounds);
724 myLayeredPane.validate();
725 myLayeredPane.repaint();
727 surface.runMovement();
728 myLayeredPane.remove(surface);
729 }else{ // not animated
730 myLayeredPane.remove(myComponent);
732 if(!myDirtyMode){
733 myLayeredPane.validate();
734 myLayeredPane.repaint();
736 }finally{
737 finish();
742 private final class SetEditorComponentCmd extends FinalizableCommand{
743 private final JComponent myComponent;
745 public SetEditorComponentCmd(final JComponent component,final Runnable finishCallBack){
746 super(finishCallBack);
747 myComponent=component;
750 public void run(){
751 try{
752 setDocumentComponent(myComponent);
753 myLayeredPane.validate();
754 myLayeredPane.repaint();
755 }finally{
756 finish();
761 private final class UpdateButtonPositionCmd extends FinalizableCommand {
762 private final String myId;
764 private UpdateButtonPositionCmd(String id, final Runnable finishCallBack) {
765 super(finishCallBack);
766 myId = id;
770 public void run() {
771 try {
772 WindowInfoImpl info = getButtonById(myId).getWindowInfo();
773 ToolWindowAnchor anchor = info.getAnchor();
775 if (ToolWindowAnchor.TOP == anchor){
776 myTopStripe.revalidate();
778 else if (ToolWindowAnchor.LEFT == anchor){
779 myLeftStripe.revalidate();
781 else if (ToolWindowAnchor.BOTTOM == anchor) {
782 myBottomStripe.revalidate();
784 else if (ToolWindowAnchor.RIGHT == anchor) {
785 myRightStripe.revalidate();
787 else {
788 LOG.error("unknown anchor: " + anchor);
790 } finally {
791 finish();
796 private final class MyUISettingsListenerImpl implements UISettingsListener{
797 public final void uiSettingsChanged(final UISettings source){
798 updateToolStripesVisibility();
802 private final class MyLayeredPane extends JLayeredPane{
804 * These images are used to perform animated showing and hiding of components.
805 * They are the member for performance reason.
807 private SoftReference myBottomImageRef;
808 private SoftReference myTopImageRef;
810 public MyLayeredPane(final JComponent splitter) {
811 myBottomImageRef=new SoftReference(null);
812 myTopImageRef=new SoftReference(null);
813 setOpaque(true);
814 setBackground(Color.gray);
815 add(splitter,JLayeredPane.DEFAULT_LAYER);
816 splitter.setBounds(0,0,getWidth(),getHeight());
817 enableEvents(ComponentEvent.COMPONENT_EVENT_MASK);
821 * TODO[vova] extract method
822 * Lazily creates and returns bottom image for animation.
824 public final Image getBottomImage(){
825 LOG.assertTrue(UISettings.getInstance().ANIMATE_WINDOWS);
826 BufferedImage image=(BufferedImage)myBottomImageRef.get();
828 image==null ||
829 image.getWidth(null) < getWidth() || image.getHeight(null) < getHeight()
831 final int width=Math.max(Math.max(1,getWidth()),myFrame.getWidth());
832 final int height=Math.max(Math.max(1,getHeight()),myFrame.getHeight());
833 if(SystemInfo.isWindows || SystemInfo.isMac){
834 image=myFrame.getGraphicsConfiguration().createCompatibleImage(width,height);
835 }else{
836 // Under Linux we have found that images created by createCompatibleImage(),
837 // createVolatileImage(), etc extremely slow for rendering. TrueColor buffered image
838 // is MUCH faster.
839 image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
841 myBottomImageRef=new SoftReference(image);
843 return image;
847 * TODO[vova] extract method
848 * Lazily creates and returns top image for animation.
850 public final Image getTopImage(){
851 LOG.assertTrue(UISettings.getInstance().ANIMATE_WINDOWS);
852 BufferedImage image=(BufferedImage)myTopImageRef.get();
854 image==null ||
855 image.getWidth(null) < getWidth() || image.getHeight(null) < getHeight()
857 final int width=Math.max(Math.max(1,getWidth()),myFrame.getWidth());
858 final int height=Math.max(Math.max(1,getHeight()),myFrame.getHeight());
859 if(SystemInfo.isWindows || SystemInfo.isMac){
860 image=myFrame.getGraphicsConfiguration().createCompatibleImage(width,height);
861 }else{
862 // Under Linux we have found that images created by createCompatibleImage(),
863 // createVolatileImage(), etc extremely slow for rendering. TrueColor buffered image
864 // is MUCH faster.
865 image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
867 myTopImageRef=new SoftReference(image);
869 return image;
873 * When component size becomes larger then bottom and top images should be enlarged.
875 protected final void processComponentEvent(final ComponentEvent e) {
876 if(ComponentEvent.COMPONENT_RESIZED==e.getID()){
877 final int width=getWidth();
878 final int height=getHeight();
879 if(width<0||height<0){
880 return;
882 // Resize component at the DEFAULT layer. It should be only on component in that layer
883 Component[] components=getComponentsInLayer(JLayeredPane.DEFAULT_LAYER.intValue());
884 LOG.assertTrue(components.length<=1);
885 for(int i=0;i<components.length;i++){
886 final Component component=components[i];
887 component.setBounds(0,0,getWidth(),getHeight());
889 // Resize components at the PALETTE layer
890 components=getComponentsInLayer(JLayeredPane.PALETTE_LAYER.intValue());
891 for(int i=0;i<components.length;i++){
892 final Component component=components[i];
893 if (!(component instanceof InternalDecorator)) {
894 continue;
896 final WindowInfoImpl info=myDecorator2Info.get(component);
897 // In normal situation info is not null. But sometimes Swing sends resize
898 // event to removed component. See SCR #19566.
899 if(info == null){
900 continue;
903 final float weight;
904 if(ToolWindowAnchor.TOP==info.getAnchor()||ToolWindowAnchor.BOTTOM==info.getAnchor()){
905 weight=(float)component.getHeight()/(float)getHeight();
906 }else{
907 weight=(float)component.getWidth()/(float)getWidth();
909 setBoundsInPaletteLayer(component,info.getAnchor(),weight);
911 validate();
912 repaint();
913 }else{
914 super.processComponentEvent(e);
918 public final void setBoundsInPaletteLayer(final Component component,final ToolWindowAnchor anchor,float weight){
919 if(weight<.0f){
920 weight= WindowInfoImpl.DEFAULT_WEIGHT;
921 }else if(weight>1.0f){
922 weight=1.0f;
924 if(ToolWindowAnchor.TOP==anchor){
925 component.setBounds(0,0,getWidth(),(int)(getHeight()*weight+.5f));
926 }else if(ToolWindowAnchor.LEFT==anchor){
927 component.setBounds(0,0,(int)(getWidth()*weight+.5f),getHeight());
928 }else if(ToolWindowAnchor.BOTTOM==anchor){
929 final int height=(int)(getHeight()*weight+.5f);
930 component.setBounds(0,getHeight()-height,getWidth(),height);
931 }else if(ToolWindowAnchor.RIGHT==anchor){
932 final int width=(int)(getWidth()*weight+.5f);
933 component.setBounds(getWidth()-width,0,width,getHeight());
934 }else{
935 LOG.error("unknown anchor "+anchor);
940 //todo
941 static int getHorizontalInsetX() {
942 return 19;