inactive floating toolwindow doesnt request focus on show()
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / FloatingDecorator.java
blobd985ae1da2dcaf96dfe9135d40096cb8f3421a83
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.MnemonicHelper;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.util.SystemInfo;
23 import com.intellij.openapi.wm.ex.WindowManagerEx;
24 import com.intellij.util.Alarm;
25 import com.intellij.util.ui.UIUtil;
27 import javax.swing.*;
28 import java.awt.*;
29 import java.awt.event.MouseEvent;
30 import java.awt.event.WindowAdapter;
31 import java.awt.event.WindowEvent;
33 /**
34 * @author Anton Katilin
35 * @author Vladimir Kondratyev
37 public final class FloatingDecorator extends JDialog{
38 private static final Logger LOG=Logger.getInstance("#com.intellij.openapi.wm.impl.FloatingDecorator");
40 private static final int ANCHOR_TOP=1;
41 private static final int ANCHOR_LEFT=2;
42 private static final int ANCHOR_BOTTOM=4;
43 private static final int ANCHOR_RIGHT=8;
45 private static final int DELAY=15; // Delay between frames
46 private static final int TOTAL_FRAME_COUNT=7; // Total number of frames in animation sequence
48 private final InternalDecorator myInternalDecorator;
49 private final MyUISettingsListener myUISettingsListener;
50 private WindowInfoImpl myInfo;
52 private final Alarm myDelayAlarm; // Determines moment when tool window should become transparent
53 private final Alarm myFrameTicker; // Determines moments of rendering of next frame
54 private final MyAnimator myAnimator; // Renders alpha ratio
55 private int myCurrentFrame; // current frame in transparency animation
56 private float myStartRatio, myEndRatio; // start and end alpha ratio for transparency animation
59 FloatingDecorator(final IdeFrameImpl owner,final WindowInfoImpl info,final InternalDecorator internalDecorator){
60 super(owner,internalDecorator.getToolWindow().getId());
61 new MnemonicHelper().register(getContentPane());
62 myInternalDecorator=internalDecorator;
64 setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
65 final JComponent cp=(JComponent)getContentPane();
66 cp.setLayout(new BorderLayout());
68 if(SystemInfo.isWindows){
69 setUndecorated(true);
70 cp.add(new BorderItem(ANCHOR_TOP),BorderLayout.NORTH);
71 cp.add(new BorderItem(ANCHOR_LEFT),BorderLayout.WEST);
72 cp.add(new BorderItem(ANCHOR_BOTTOM),BorderLayout.SOUTH);
73 cp.add(new BorderItem(ANCHOR_RIGHT),BorderLayout.EAST);
74 cp.add(myInternalDecorator,BorderLayout.CENTER);
75 }else{
76 // Due to JDK's bug #4234645 we cannot support custom decoration on Linux platform.
77 // The prblem is that Window.setLocation() doesn't work properly wjen the dialod is displayable.
78 // Therefore we use native WM decoration.
79 // TODO[vova] investigate the problem under Mac OSX.
80 cp.add(myInternalDecorator,BorderLayout.CENTER);
81 getRootPane().putClientProperty("Window.style", "small");
84 setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
85 addWindowListener(new MyWindowListener());
89 myDelayAlarm=new Alarm();
90 myFrameTicker=new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
91 myAnimator=new MyAnimator();
92 myCurrentFrame=0;
93 myStartRatio=0.0f;
94 myEndRatio=0.0f;
96 myUISettingsListener=new MyUISettingsListener();
100 getRootPane().setGlassPane(new IdeGlassPaneImpl(getRootPane()));
102 apply(info);
105 public final void show(){
106 setFocusableWindowState(myInfo.isActive());
108 super.show();
109 final UISettings uiSettings=UISettings.getInstance();
110 if(uiSettings.ENABLE_ALPHA_MODE){
111 final WindowManagerEx windowManager=WindowManagerEx.getInstanceEx();
112 windowManager.setAlphaModeEnabled(this,true);
113 if(myInfo.isActive()){
114 windowManager.setAlphaModeRatio(this,0.0f);
115 }else{
116 windowManager.setAlphaModeRatio(this,uiSettings.ALPHA_MODE_RATIO);
119 paint(getGraphics()); // This prevents annoying flick
122 setFocusableWindowState(true);
124 uiSettings.addUISettingsListener(myUISettingsListener);
127 public final void dispose(){
128 UISettings.getInstance().removeUISettingsListener(myUISettingsListener);
129 super.dispose();
132 final void apply(final WindowInfoImpl info){
133 LOG.assertTrue(info.isFloating());
134 myInfo=info;
135 // Set alpha mode
136 final UISettings uiSettings=UISettings.getInstance();
137 if(uiSettings.ENABLE_ALPHA_MODE&&isShowing()&&isDisplayable()){
138 myDelayAlarm.cancelAllRequests();
139 if(myInfo.isActive()){ // make window non transparent
140 myFrameTicker.cancelAllRequests();
141 myStartRatio=getCurrentAlphaRatio();
142 if(myCurrentFrame>0){
143 myCurrentFrame=TOTAL_FRAME_COUNT-myCurrentFrame;
145 myEndRatio=.0f;
146 myFrameTicker.addRequest(myAnimator,DELAY);
147 }else{ // make window transparent
148 myDelayAlarm.addRequest(
149 new Runnable(){
150 public void run(){
151 myFrameTicker.cancelAllRequests();
152 myStartRatio=getCurrentAlphaRatio();
153 if(myCurrentFrame>0){
154 myCurrentFrame=TOTAL_FRAME_COUNT-myCurrentFrame;
156 myEndRatio=uiSettings.ALPHA_MODE_RATIO;
157 myFrameTicker.addRequest(myAnimator,DELAY);
160 uiSettings.ALPHA_MODE_DELAY
166 private float getCurrentAlphaRatio(){
167 float delta=(myEndRatio-myStartRatio)/(float)TOTAL_FRAME_COUNT;
168 if(myStartRatio>myEndRatio){ // dialog is becoming non transparent quicker
169 delta*=2;
171 final float ratio=myStartRatio+(float)myCurrentFrame*delta;
172 return Math.min(1.0f,Math.max(.0f,ratio));
175 private final class BorderItem extends JPanel{
176 private static final int DIVIDER_WIDTH=3;
177 private static final int RESIZER_WIDTH=10;
179 private final int myAnchor;
180 private int myMotionMask;
181 private Point myLastPoint;
182 private boolean myDragging;
184 public BorderItem(final int anchor){
185 myAnchor=anchor;
186 enableEvents(MouseEvent.MOUSE_EVENT_MASK|MouseEvent.MOUSE_MOTION_EVENT_MASK);
189 protected final void processMouseMotionEvent(final MouseEvent e){
190 super.processMouseMotionEvent(e);
191 if(MouseEvent.MOUSE_DRAGGED==e.getID() && myLastPoint != null){
192 final Point newPoint=e.getPoint();
193 SwingUtilities.convertPointToScreen(newPoint,this);
194 final Rectangle screenBounds=WindowManagerEx.getInstanceEx().getScreenBounds();
196 newPoint.x=Math.min(Math.max(newPoint.x,screenBounds.x),screenBounds.width);
197 newPoint.y=Math.min(Math.max(newPoint.y,screenBounds.y),screenBounds.height);
199 final Rectangle oldBounds=FloatingDecorator.this.getBounds();
200 final Rectangle newBounds=new Rectangle(oldBounds);
202 if((myMotionMask&ANCHOR_TOP)>0){
203 newPoint.y=Math.min(newPoint.y,oldBounds.y+oldBounds.height-2*DIVIDER_WIDTH);
204 if(newPoint.y<screenBounds.y+DIVIDER_WIDTH){
205 newPoint.y=screenBounds.y;
207 final Point offset=new Point(newPoint.x-myLastPoint.x,newPoint.y-myLastPoint.y);
208 newBounds.y=oldBounds.y+offset.y;
209 newBounds.height=oldBounds.height-offset.y;
211 if((myMotionMask&ANCHOR_LEFT)>0){
212 newPoint.x=Math.min(newPoint.x,oldBounds.x+oldBounds.width-2*DIVIDER_WIDTH);
213 if(newPoint.x<screenBounds.x+DIVIDER_WIDTH){
214 newPoint.x=screenBounds.x;
216 final Point offset=new Point(newPoint.x-myLastPoint.x,newPoint.y-myLastPoint.y);
217 newBounds.x=oldBounds.x+offset.x;
218 newBounds.width=oldBounds.width-offset.x;
220 if((myMotionMask&ANCHOR_BOTTOM)>0){
221 newPoint.y=Math.max(newPoint.y,oldBounds.y+2*DIVIDER_WIDTH);
222 if(newPoint.y>screenBounds.height-DIVIDER_WIDTH){
223 newPoint.y=screenBounds.height;
225 final Point offset=new Point(newPoint.x-myLastPoint.x,newPoint.y-myLastPoint.y);
226 newBounds.height=oldBounds.height+offset.y;
228 if((myMotionMask&ANCHOR_RIGHT)>0){
229 newPoint.x=Math.max(newPoint.x,oldBounds.x+2*DIVIDER_WIDTH);
230 if(newPoint.x>screenBounds.width-DIVIDER_WIDTH){
231 newPoint.x=screenBounds.width;
233 final Point offset=new Point(newPoint.x-myLastPoint.x,newPoint.y-myLastPoint.y);
234 newBounds.width=oldBounds.width+offset.x;
236 // It's much better to resize frame this way then via Component.setBounds() method.
237 // Component.setBounds() method cause annoying repainting and blinking.
238 //FloatingDecorator.this.getPeer().setBounds(newBounds.x,newBounds.y,newBounds.width,newBounds.height, 0);
239 FloatingDecorator.this.setBounds(newBounds.x,newBounds.y,newBounds.width,newBounds.height);
241 myLastPoint=newPoint;
242 } else if(e.getID()==MouseEvent.MOUSE_MOVED){
243 if(!myDragging){
244 setMotionMask(e.getPoint());
249 protected final void processMouseEvent(final MouseEvent e){
250 super.processMouseEvent(e);
251 switch(e.getID()){
252 case MouseEvent.MOUSE_PRESSED:{
253 myLastPoint=e.getPoint();
254 SwingUtilities.convertPointToScreen(myLastPoint,this);
255 setMotionMask(e.getPoint());
256 myDragging=true;
257 break;
258 }case MouseEvent.MOUSE_RELEASED:{
259 FloatingDecorator.this.validate();
260 FloatingDecorator.this.repaint();
261 myDragging=false;
262 break;
263 }case MouseEvent.MOUSE_ENTERED:{
264 if(!myDragging){
265 setMotionMask(e.getPoint());
271 private void setMotionMask(final Point p){
272 myMotionMask=myAnchor;
273 if(ANCHOR_TOP==myAnchor||ANCHOR_BOTTOM==myAnchor){
274 if(p.getX()<RESIZER_WIDTH){
275 myMotionMask|=ANCHOR_LEFT;
276 } else if(p.getX()>getWidth()-RESIZER_WIDTH){
277 myMotionMask|=ANCHOR_RIGHT;
279 } else{
280 if(p.getY()<RESIZER_WIDTH){
281 myMotionMask|=ANCHOR_TOP;
282 } else if(p.getY()>getHeight()-RESIZER_WIDTH){
283 myMotionMask|=ANCHOR_BOTTOM;
286 if(myMotionMask==ANCHOR_TOP){
287 setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
288 } else if(myMotionMask==(ANCHOR_TOP|ANCHOR_LEFT)){
289 setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
290 } else if(myMotionMask==ANCHOR_LEFT){
291 setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
292 } else if(myMotionMask==(ANCHOR_LEFT|ANCHOR_BOTTOM)){
293 setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
294 } else if(myMotionMask==ANCHOR_BOTTOM){
295 setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
296 } else if(myMotionMask==(ANCHOR_BOTTOM|ANCHOR_RIGHT)){
297 setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
298 } else if(myMotionMask==ANCHOR_RIGHT){
299 setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
300 } else if(myMotionMask==(ANCHOR_RIGHT|ANCHOR_TOP)){
301 setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
305 public final Dimension getPreferredSize(){
306 final Dimension d=super.getPreferredSize();
307 if(ANCHOR_TOP==myAnchor||ANCHOR_BOTTOM==myAnchor){
308 d.height=DIVIDER_WIDTH;
309 } else{
310 d.width=DIVIDER_WIDTH;
312 return d;
315 public final void paint(final Graphics g){
316 super.paint(g);
317 if(ANCHOR_TOP==myAnchor){
318 g.setColor(Color.lightGray);
319 UIUtil.drawLine(g, 0, 0, getWidth() - 1, 0);
320 UIUtil.drawLine(g, 0, 0, 0, getHeight() - 1);
321 g.setColor(Color.gray);
322 UIUtil.drawLine(g, getWidth() - 1, 0, getWidth() - 1, getHeight() - 1);
323 } else if(ANCHOR_LEFT==myAnchor){
324 g.setColor(Color.lightGray);
325 UIUtil.drawLine(g, 0, 0, 0, getHeight() - 1);
326 } else if(ANCHOR_BOTTOM==myAnchor){
327 g.setColor(Color.lightGray);
328 UIUtil.drawLine(g, 0, 0, 0, getHeight() - 1);
329 g.setColor(Color.gray);
330 UIUtil.drawLine(g, 0, getHeight() - 1, getWidth() - 1, getHeight() - 1);
331 UIUtil.drawLine(g, getWidth() - 1, 0, getWidth() - 1, getHeight() - 1);
332 } else{ // RIGHT
333 g.setColor(Color.gray);
334 UIUtil.drawLine(g, getWidth() - 1, 0, getWidth() - 1, getHeight() - 1);
339 private final class MyWindowListener extends WindowAdapter{
340 public void windowClosing(final WindowEvent e){
341 myInternalDecorator.fireResized();
342 myInternalDecorator.fireHidden();
346 private final class MyAnimator implements Runnable{
347 public final void run(){
348 final WindowManagerEx windowManager=WindowManagerEx.getInstanceEx();
349 if(isDisplayable()&&isShowing()){
350 windowManager.setAlphaModeRatio(FloatingDecorator.this,getCurrentAlphaRatio());
352 if(myCurrentFrame<TOTAL_FRAME_COUNT){
353 myCurrentFrame++;
354 myFrameTicker.addRequest(myAnimator,DELAY);
355 }else{
356 myFrameTicker.cancelAllRequests();
361 private final class MyUISettingsListener implements UISettingsListener{
362 public void uiSettingsChanged(final UISettings uiSettings){
363 LOG.assertTrue(isDisplayable());
364 LOG.assertTrue(isShowing());
365 final WindowManagerEx windowManager=WindowManagerEx.getInstanceEx();
366 myDelayAlarm.cancelAllRequests();
367 if(uiSettings.ENABLE_ALPHA_MODE){
368 if(!myInfo.isActive()){
369 windowManager.setAlphaModeEnabled(FloatingDecorator.this,true);
370 windowManager.setAlphaModeRatio(FloatingDecorator.this,uiSettings.ALPHA_MODE_RATIO);
372 }else{
373 windowManager.setAlphaModeEnabled(FloatingDecorator.this,false);