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(){
107 final UISettings uiSettings
=UISettings
.getInstance();
108 if(uiSettings
.ENABLE_ALPHA_MODE
){
109 final WindowManagerEx windowManager
=WindowManagerEx
.getInstanceEx();
110 windowManager
.setAlphaModeEnabled(this,true);
111 if(myInfo
.isActive()){
112 windowManager
.setAlphaModeRatio(this,0.0f
);
114 windowManager
.setAlphaModeRatio(this,uiSettings
.ALPHA_MODE_RATIO
);
117 paint(getGraphics()); // This prevents annoying flick
121 uiSettings
.addUISettingsListener(myUISettingsListener
);
124 public final void dispose(){
125 UISettings
.getInstance().removeUISettingsListener(myUISettingsListener
);
129 final void apply(final WindowInfoImpl info
){
130 LOG
.assertTrue(info
.isFloating());
133 final UISettings uiSettings
=UISettings
.getInstance();
134 if(uiSettings
.ENABLE_ALPHA_MODE
&&isShowing()&&isDisplayable()){
135 myDelayAlarm
.cancelAllRequests();
136 if(myInfo
.isActive()){ // make window non transparent
137 myFrameTicker
.cancelAllRequests();
138 myStartRatio
=getCurrentAlphaRatio();
139 if(myCurrentFrame
>0){
140 myCurrentFrame
=TOTAL_FRAME_COUNT
-myCurrentFrame
;
143 myFrameTicker
.addRequest(myAnimator
,DELAY
);
144 }else{ // make window transparent
145 myDelayAlarm
.addRequest(
148 myFrameTicker
.cancelAllRequests();
149 myStartRatio
=getCurrentAlphaRatio();
150 if(myCurrentFrame
>0){
151 myCurrentFrame
=TOTAL_FRAME_COUNT
-myCurrentFrame
;
153 myEndRatio
=uiSettings
.ALPHA_MODE_RATIO
;
154 myFrameTicker
.addRequest(myAnimator
,DELAY
);
157 uiSettings
.ALPHA_MODE_DELAY
163 private float getCurrentAlphaRatio(){
164 float delta
=(myEndRatio
-myStartRatio
)/(float)TOTAL_FRAME_COUNT
;
165 if(myStartRatio
>myEndRatio
){ // dialog is becoming non transparent quicker
168 final float ratio
=myStartRatio
+(float)myCurrentFrame
*delta
;
169 return Math
.min(1.0f
,Math
.max(.0f
,ratio
));
172 private final class BorderItem
extends JPanel
{
173 private static final int DIVIDER_WIDTH
=3;
174 private static final int RESIZER_WIDTH
=10;
176 private final int myAnchor
;
177 private int myMotionMask
;
178 private Point myLastPoint
;
179 private boolean myDragging
;
181 public BorderItem(final int anchor
){
183 enableEvents(MouseEvent
.MOUSE_EVENT_MASK
|MouseEvent
.MOUSE_MOTION_EVENT_MASK
);
186 protected final void processMouseMotionEvent(final MouseEvent e
){
187 super.processMouseMotionEvent(e
);
188 if(MouseEvent
.MOUSE_DRAGGED
==e
.getID() && myLastPoint
!= null){
189 final Point newPoint
=e
.getPoint();
190 SwingUtilities
.convertPointToScreen(newPoint
,this);
191 final Rectangle screenBounds
=WindowManagerEx
.getInstanceEx().getScreenBounds();
193 newPoint
.x
=Math
.min(Math
.max(newPoint
.x
,screenBounds
.x
),screenBounds
.width
);
194 newPoint
.y
=Math
.min(Math
.max(newPoint
.y
,screenBounds
.y
),screenBounds
.height
);
196 final Rectangle oldBounds
=FloatingDecorator
.this.getBounds();
197 final Rectangle newBounds
=new Rectangle(oldBounds
);
199 if((myMotionMask
&ANCHOR_TOP
)>0){
200 newPoint
.y
=Math
.min(newPoint
.y
,oldBounds
.y
+oldBounds
.height
-2*DIVIDER_WIDTH
);
201 if(newPoint
.y
<screenBounds
.y
+DIVIDER_WIDTH
){
202 newPoint
.y
=screenBounds
.y
;
204 final Point offset
=new Point(newPoint
.x
-myLastPoint
.x
,newPoint
.y
-myLastPoint
.y
);
205 newBounds
.y
=oldBounds
.y
+offset
.y
;
206 newBounds
.height
=oldBounds
.height
-offset
.y
;
208 if((myMotionMask
&ANCHOR_LEFT
)>0){
209 newPoint
.x
=Math
.min(newPoint
.x
,oldBounds
.x
+oldBounds
.width
-2*DIVIDER_WIDTH
);
210 if(newPoint
.x
<screenBounds
.x
+DIVIDER_WIDTH
){
211 newPoint
.x
=screenBounds
.x
;
213 final Point offset
=new Point(newPoint
.x
-myLastPoint
.x
,newPoint
.y
-myLastPoint
.y
);
214 newBounds
.x
=oldBounds
.x
+offset
.x
;
215 newBounds
.width
=oldBounds
.width
-offset
.x
;
217 if((myMotionMask
&ANCHOR_BOTTOM
)>0){
218 newPoint
.y
=Math
.max(newPoint
.y
,oldBounds
.y
+2*DIVIDER_WIDTH
);
219 if(newPoint
.y
>screenBounds
.height
-DIVIDER_WIDTH
){
220 newPoint
.y
=screenBounds
.height
;
222 final Point offset
=new Point(newPoint
.x
-myLastPoint
.x
,newPoint
.y
-myLastPoint
.y
);
223 newBounds
.height
=oldBounds
.height
+offset
.y
;
225 if((myMotionMask
&ANCHOR_RIGHT
)>0){
226 newPoint
.x
=Math
.max(newPoint
.x
,oldBounds
.x
+2*DIVIDER_WIDTH
);
227 if(newPoint
.x
>screenBounds
.width
-DIVIDER_WIDTH
){
228 newPoint
.x
=screenBounds
.width
;
230 final Point offset
=new Point(newPoint
.x
-myLastPoint
.x
,newPoint
.y
-myLastPoint
.y
);
231 newBounds
.width
=oldBounds
.width
+offset
.x
;
233 // It's much better to resize frame this way then via Component.setBounds() method.
234 // Component.setBounds() method cause annoying repainting and blinking.
235 //FloatingDecorator.this.getPeer().setBounds(newBounds.x,newBounds.y,newBounds.width,newBounds.height, 0);
236 FloatingDecorator
.this.setBounds(newBounds
.x
,newBounds
.y
,newBounds
.width
,newBounds
.height
);
238 myLastPoint
=newPoint
;
239 } else if(e
.getID()==MouseEvent
.MOUSE_MOVED
){
241 setMotionMask(e
.getPoint());
246 protected final void processMouseEvent(final MouseEvent e
){
247 super.processMouseEvent(e
);
249 case MouseEvent
.MOUSE_PRESSED
:{
250 myLastPoint
=e
.getPoint();
251 SwingUtilities
.convertPointToScreen(myLastPoint
,this);
252 setMotionMask(e
.getPoint());
255 }case MouseEvent
.MOUSE_RELEASED
:{
256 FloatingDecorator
.this.validate();
257 FloatingDecorator
.this.repaint();
260 }case MouseEvent
.MOUSE_ENTERED
:{
262 setMotionMask(e
.getPoint());
268 private void setMotionMask(final Point p
){
269 myMotionMask
=myAnchor
;
270 if(ANCHOR_TOP
==myAnchor
||ANCHOR_BOTTOM
==myAnchor
){
271 if(p
.getX()<RESIZER_WIDTH
){
272 myMotionMask
|=ANCHOR_LEFT
;
273 } else if(p
.getX()>getWidth()-RESIZER_WIDTH
){
274 myMotionMask
|=ANCHOR_RIGHT
;
277 if(p
.getY()<RESIZER_WIDTH
){
278 myMotionMask
|=ANCHOR_TOP
;
279 } else if(p
.getY()>getHeight()-RESIZER_WIDTH
){
280 myMotionMask
|=ANCHOR_BOTTOM
;
283 if(myMotionMask
==ANCHOR_TOP
){
284 setCursor(Cursor
.getPredefinedCursor(Cursor
.N_RESIZE_CURSOR
));
285 } else if(myMotionMask
==(ANCHOR_TOP
|ANCHOR_LEFT
)){
286 setCursor(Cursor
.getPredefinedCursor(Cursor
.NW_RESIZE_CURSOR
));
287 } else if(myMotionMask
==ANCHOR_LEFT
){
288 setCursor(Cursor
.getPredefinedCursor(Cursor
.W_RESIZE_CURSOR
));
289 } else if(myMotionMask
==(ANCHOR_LEFT
|ANCHOR_BOTTOM
)){
290 setCursor(Cursor
.getPredefinedCursor(Cursor
.SW_RESIZE_CURSOR
));
291 } else if(myMotionMask
==ANCHOR_BOTTOM
){
292 setCursor(Cursor
.getPredefinedCursor(Cursor
.S_RESIZE_CURSOR
));
293 } else if(myMotionMask
==(ANCHOR_BOTTOM
|ANCHOR_RIGHT
)){
294 setCursor(Cursor
.getPredefinedCursor(Cursor
.SE_RESIZE_CURSOR
));
295 } else if(myMotionMask
==ANCHOR_RIGHT
){
296 setCursor(Cursor
.getPredefinedCursor(Cursor
.E_RESIZE_CURSOR
));
297 } else if(myMotionMask
==(ANCHOR_RIGHT
|ANCHOR_TOP
)){
298 setCursor(Cursor
.getPredefinedCursor(Cursor
.NE_RESIZE_CURSOR
));
302 public final Dimension
getPreferredSize(){
303 final Dimension d
=super.getPreferredSize();
304 if(ANCHOR_TOP
==myAnchor
||ANCHOR_BOTTOM
==myAnchor
){
305 d
.height
=DIVIDER_WIDTH
;
307 d
.width
=DIVIDER_WIDTH
;
312 public final void paint(final Graphics g
){
314 if(ANCHOR_TOP
==myAnchor
){
315 g
.setColor(Color
.lightGray
);
316 UIUtil
.drawLine(g
, 0, 0, getWidth() - 1, 0);
317 UIUtil
.drawLine(g
, 0, 0, 0, getHeight() - 1);
318 g
.setColor(Color
.gray
);
319 UIUtil
.drawLine(g
, getWidth() - 1, 0, getWidth() - 1, getHeight() - 1);
320 } else if(ANCHOR_LEFT
==myAnchor
){
321 g
.setColor(Color
.lightGray
);
322 UIUtil
.drawLine(g
, 0, 0, 0, getHeight() - 1);
323 } else if(ANCHOR_BOTTOM
==myAnchor
){
324 g
.setColor(Color
.lightGray
);
325 UIUtil
.drawLine(g
, 0, 0, 0, getHeight() - 1);
326 g
.setColor(Color
.gray
);
327 UIUtil
.drawLine(g
, 0, getHeight() - 1, getWidth() - 1, getHeight() - 1);
328 UIUtil
.drawLine(g
, getWidth() - 1, 0, getWidth() - 1, getHeight() - 1);
330 g
.setColor(Color
.gray
);
331 UIUtil
.drawLine(g
, getWidth() - 1, 0, getWidth() - 1, getHeight() - 1);
336 private final class MyWindowListener
extends WindowAdapter
{
337 public void windowClosing(final WindowEvent e
){
338 myInternalDecorator
.fireResized();
339 myInternalDecorator
.fireHidden();
343 private final class MyAnimator
implements Runnable
{
344 public final void run(){
345 final WindowManagerEx windowManager
=WindowManagerEx
.getInstanceEx();
346 if(isDisplayable()&&isShowing()){
347 windowManager
.setAlphaModeRatio(FloatingDecorator
.this,getCurrentAlphaRatio());
349 if(myCurrentFrame
<TOTAL_FRAME_COUNT
){
351 myFrameTicker
.addRequest(myAnimator
,DELAY
);
353 myFrameTicker
.cancelAllRequests();
358 private final class MyUISettingsListener
implements UISettingsListener
{
359 public void uiSettingsChanged(final UISettings uiSettings
){
360 LOG
.assertTrue(isDisplayable());
361 LOG
.assertTrue(isShowing());
362 final WindowManagerEx windowManager
=WindowManagerEx
.getInstanceEx();
363 myDelayAlarm
.cancelAllRequests();
364 if(uiSettings
.ENABLE_ALPHA_MODE
){
365 if(!myInfo
.isActive()){
366 windowManager
.setAlphaModeEnabled(FloatingDecorator
.this,true);
367 windowManager
.setAlphaModeRatio(FloatingDecorator
.this,uiSettings
.ALPHA_MODE_RATIO
);
370 windowManager
.setAlphaModeEnabled(FloatingDecorator
.this,false);