1  /*
     2   Copyright aswing.org, see the LICENCE.txt.
     3  */
     4  import org.aswing.ASWingUtils;
     5  import org.aswing.Component;
     6  import org.aswing.Container;
     7  import org.aswing.EmptyLayout;
     8  import org.aswing.Event;
     9  import org.aswing.geom.Dimension;
    10  import org.aswing.geom.Point;
    11  import org.aswing.geom.Rectangle;
    12  import org.aswing.MCPanel;
    13  import org.aswing.plaf.ToolTipUI;
    14  import org.aswing.UIManager;
    15  import org.aswing.utils.Delegate;
    16  import org.aswing.utils.DepthManager;
    17  import org.aswing.utils.Timer;
    18  
    19  /**
    20   *
    21   * @author iiley
    22   */
    23  class org.aswing.JToolTip extends Container {
    24  	
    25  	/**
    26  	 * When the tip text changed.
    27  	 *<br>
    28  	 * onTipTextChanged Event{source:JTextComponent}
    29  	 */	
    30  	public static var ON_TIP_TEXT_CHANGED :String = "onTipTextChanged";
    31  	
    32  	
    33  	//the time waiting after to view tool tip when roll over a component
    34  	private static var WAIT_TIME:Number = 600;
    35  	//when there is one tooltip is just shown, next will shown fast as this time
    36  	private static var FAST_OCCUR_TIME:Number = 50;
    37  	
    38  	private static var last_tip_dropped_time:Number = 0;
    39  	
    40  	private static var mcPane:MCPanel;
    41  	
    42  	private var tipText:String;
    43  	private var comp:Component;
    44  	private var offsets:Point;
    45  	private var offsetsRelatedToMouse:Boolean;
    46  	
    47  	private var compListener:Object;
    48  	private var mouseMovedListener:Object;
    49  	private var timer:Timer;
    50  	
    51  	public function JToolTip() {
    52  		super();
    53  		setName("JToolTip");
    54  		offsets = new Point(4, 20);
    55  		offsetsRelatedToMouse = true;
    56  		
    57  		compListener = new Object();
    58  		compListener[Component.ON_ROLLOVER] = Delegate.create(this, __compRollOver);
    59  		compListener[Component.ON_ROLLOUT] = Delegate.create(this, __compRollOut);
    60  		compListener[Component.ON_HIDDEN] = compListener[Component.ON_ROLLOUT];
    61  		compListener[Component.ON_DESTROY] = compListener[Component.ON_ROLLOUT];
    62  		compListener[Component.ON_PRESS] = compListener[Component.ON_ROLLOUT];
    63  		
    64  		mouseMovedListener = new Object();
    65  		mouseMovedListener.onMouseMove = Delegate.create(this, __onMouseMoved);
    66  		
    67  		timer = new Timer(Number.POSITIVE_INFINITY);
    68  		timer.setRepeats(false);
    69  		timer.setInitialDelay(WAIT_TIME);
    70  		timer.addActionListener(__timeOnAction, this);
    71  		
    72  		if(mcPane == undefined){
    73  			mcPane = new MCPanel(ASWingUtils.getRootMovieClip(), 10000, 10000);
    74  			mcPane.setLayout(new EmptyLayout());
    75  		}
    76  		
    77  		updateUI();
    78  	}
    79  	
    80  	private function __compRollOver(e:Event):Void{
    81  		if(e.getSource() == comp){
    82  			if(getTimer() - last_tip_dropped_time < FAST_OCCUR_TIME){
    83  				timer.setInitialDelay(FAST_OCCUR_TIME);
    84  			}else{
    85  				timer.setInitialDelay(WAIT_TIME);
    86  			}
    87  			timer.start();
    88  			Mouse.addListener(mouseMovedListener);
    89  		}
    90  	}
    91  	
    92  	private function __compRollOut(e:Event):Void{
    93  		if(e.getSource() == comp){
    94  			timer.stop();
    95  			Mouse.removeListener(mouseMovedListener);
    96  			disposeToolTip();
    97  			last_tip_dropped_time = getTimer();
    98  		}
    99  	}
   100  	
   101  	private function __onMouseMoved():Void{
   102  		if(timer.isRunning()){
   103  			timer.restart();
   104  		}
   105  	}
   106  	
   107  	private function __timeOnAction():Void{
   108  		timer.stop();
   109  		disposeToolTip();
   110  		viewToolTip();
   111  	}
   112  	
   113  	/**
   114  	 * view the tool tip on stage
   115  	 */
   116  	private function viewToolTip():Void{
   117  		mcPane.append(this);
   118  		var paneMC:MovieClip = mcPane.getPanelMC();
   119  		var p:Point = new Point();
   120  		
   121  		var relatePoint:Point = new Point();
   122  		if(offsetsRelatedToMouse){
   123  			relatePoint.setLocation(paneMC._xmouse, paneMC._ymouse);
   124  		}else{
   125  			relatePoint.setLocation(comp.componentToGlobal());
   126  			paneMC.globalToLocal(p);
   127  		}
   128  		p.setLocation(relatePoint);
   129  		p.move(offsets.x, offsets.y);
   130  		
   131  		var globalPos:Point = mcPane.componentToGlobal(p);
   132  		var viewSize:Dimension = getPreferredSize();
   133  		var visibleBounds:Rectangle = ASWingUtils.getVisibleMaximizedBounds();
   134  		
   135  		if(globalPos.x + viewSize.width > visibleBounds.x + visibleBounds.width){
   136  			globalPos.x = visibleBounds.x + visibleBounds.width - viewSize.width;
   137  		}
   138  		if(globalPos.y + viewSize.height > visibleBounds.y + visibleBounds.height){
   139  			globalPos.y = visibleBounds.y + visibleBounds.height - viewSize.height;
   140  		}
   141  		if(globalPos.x < visibleBounds.x){
   142  			globalPos.x = visibleBounds.x;
   143  		}
   144  		if(globalPos.y < visibleBounds.y){
   145  			globalPos.y = visibleBounds.y;
   146  		}
   147  		
   148  		setGlobalLocation(globalPos);
   149  		setSize(viewSize);
   150  		DepthManager.bringToTop(root_mc);
   151  	}
   152  	
   153  	private function disposeToolTip():Void{
   154  		removeFromContainer();
   155  	}
   156  	
   157      public function updateUI():Void{
   158      	setUI(ToolTipUI(UIManager.getUI(this)));
   159      }
   160      
   161      public function setUI(newUI:ToolTipUI):Void{
   162      	super.setUI(newUI);
   163      }
   164  	
   165  	public function getUIClassID():String{
   166  		return "ToolTipUI";
   167  	}
   168  		
   169  	/**
   170  	 * Sets the text to show when the tool tip is displayed. 
   171  	 * The string tipText may be null.
   172  	 * @param t the String to display
   173  	 */
   174  	public function setTipText(t:String):Void{
   175  		if(t != tipText){
   176  			tipText = t;
   177  			dispatchEvent(ON_TIP_TEXT_CHANGED, createEventObj(ON_TIP_TEXT_CHANGED));
   178  			repaint();
   179  			revalidate();
   180  		}
   181  	}
   182  	
   183  	/**
   184  	 * Returns the text that is shown when the tool tip is displayed. 
   185  	 * The returned value may be null. 
   186  	 * @return the string that displayed.
   187  	 */
   188  	public function getTipText():String{
   189  		return tipText;
   190  	}
   191  	
   192  	/**
   193  	 * Specifies the component that the tooltip describes. 
   194  	 * The component c may be null and will have no effect. 
   195  	 * @param the JComponent being described
   196  	 */
   197  	public function setComponent(c:Component):Void{
   198  		if(c != comp){
   199  			comp.removeEventListener(compListener);
   200  			comp = c;
   201  			comp.addEventListener(compListener);
   202  		}
   203  	}
   204  	
   205  	/**
   206  	 * Returns the component the tooltip applies to. 
   207  	 * The returned value may be null. 
   208  	 * @return the component that the tooltip describes
   209  	 */
   210  	public function getComponent():Component{
   211  		return comp;
   212  	}
   213  	
   214  	/**
   215  	 * Sets the offsets of the tooltip related the described component.
   216  	 * @param o the offsets point, delta x is o.x, delta y is o.y
   217  	 */
   218  	public function setOffsets(o:Point):Void{
   219  		offsets.setLocation(o);
   220  	}
   221  	
   222  	/**
   223  	 * Returns the offsets of the tooltip related the described component.
   224  	 * @return the offsets point, delta x is o.x, delta y is o.y
   225  	 */	
   226  	public function getOffsets():Point{
   227  		return new Point(offsets);
   228  	}
   229  	
   230  	/**
   231  	 * Sets whether the <code>offsets</code> is related the mouse position, otherwise 
   232  	 * it will be related the described component position.
   233  	 * <p>
   234  	 * This change will be taked effect at the next showing, current showing will no be changed.
   235  	 * @param b whether the <code>offsets</code> is related the mouse position
   236  	 */
   237  	public function setOffsetsRelatedToMouse(b:Boolean):Void{
   238  		offsetsRelatedToMouse = b;
   239  	}
   240  	
   241  	/**
   242  	 * Returns whether the <code>offsets</code> is related the mouse position.
   243  	 * @return whether the <code>offsets</code> is related the mouse position
   244  	 * @see #setOffsetsRelatedToMouse()
   245  	 */
   246  	public function isOffsetsRelatedToMouse():Boolean{
   247  		return offsetsRelatedToMouse;
   248  	}
   249  
   250  }
   251