55e5329c5320a0d02dba0ea95dbd560da797c70c
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / wm / impl / FloatingDecorator.java
blob55e5329c5320a0d02dba0ea95dbd560da797c70c
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 super.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);
113 }else{
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);
126 super.dispose();
129 final void apply(final WindowInfoImpl info){
130 LOG.assertTrue(info.isFloating());
131 myInfo=info;
132 // Set alpha mode
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;
142 myEndRatio=.0f;
143 myFrameTicker.addRequest(myAnimator,DELAY);
144 }else{ // make window transparent
145 myDelayAlarm.addRequest(
146 new Runnable(){
147 public void run(){
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
166 delta*=2;
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){
182 myAnchor=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){
240 if(!myDragging){
241 setMotionMask(e.getPoint());
246 protected final void processMouseEvent(final MouseEvent e){
247 super.processMouseEvent(e);
248 switch(e.getID()){
249 case MouseEvent.MOUSE_PRESSED:{
250 myLastPoint=e.getPoint();
251 SwingUtilities.convertPointToScreen(myLastPoint,this);
252 setMotionMask(e.getPoint());
253 myDragging=true;
254 break;
255 }case MouseEvent.MOUSE_RELEASED:{
256 FloatingDecorator.this.validate();
257 FloatingDecorator.this.repaint();
258 myDragging=false;
259 break;
260 }case MouseEvent.MOUSE_ENTERED:{
261 if(!myDragging){
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;
276 } else{
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;
306 } else{
307 d.width=DIVIDER_WIDTH;
309 return d;
312 public final void paint(final Graphics g){
313 super.paint(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);
329 } else{ // RIGHT
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){
350 myCurrentFrame++;
351 myFrameTicker.addRequest(myAnimator,DELAY);
352 }else{
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);
369 }else{
370 windowManager.setAlphaModeEnabled(FloatingDecorator.this,false);