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
;
29 import java
.awt
.event
.MouseEvent
;
30 import java
.awt
.event
.WindowAdapter
;
31 import java
.awt
.event
.WindowEvent
;
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
){
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
);
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();
96 myUISettingsListener
=new MyUISettingsListener();
100 getRootPane().setGlassPane(new IdeGlassPaneImpl(getRootPane()));
105 public final void show(){
106 setFocusableWindowState(myInfo
.isActive());
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
);
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
);
132 final void apply(final WindowInfoImpl info
){
133 LOG
.assertTrue(info
.isFloating());
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
;
146 myFrameTicker
.addRequest(myAnimator
,DELAY
);
147 }else{ // make window transparent
148 myDelayAlarm
.addRequest(
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
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
){
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
){
244 setMotionMask(e
.getPoint());
249 protected final void processMouseEvent(final MouseEvent e
){
250 super.processMouseEvent(e
);
252 case MouseEvent
.MOUSE_PRESSED
:{
253 myLastPoint
=e
.getPoint();
254 SwingUtilities
.convertPointToScreen(myLastPoint
,this);
255 setMotionMask(e
.getPoint());
258 }case MouseEvent
.MOUSE_RELEASED
:{
259 FloatingDecorator
.this.validate();
260 FloatingDecorator
.this.repaint();
263 }case MouseEvent
.MOUSE_ENTERED
:{
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
;
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
;
310 d
.width
=DIVIDER_WIDTH
;
315 public final void paint(final Graphics 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);
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
){
354 myFrameTicker
.addRequest(myAnimator
,DELAY
);
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
);
373 windowManager
.setAlphaModeEnabled(FloatingDecorator
.this,false);