/*
 * COPYRIGHT (c) 1999, Jan-Henrik Haukeland <hauk@tildeslash.com>
 */

package ts.ui;

import java.awt.*;
import java.util.*;
import java.awt.event.*;


/**  
 * A window manager singelton, that can handle any number of Windows.
 * This manager handles window move and resize only. A window resize 
 * is conducted iff the mouse cursor is over the resize hotspot. 
 * This hotspot is always placed in the lower right corner by the 
 * manager, for every window it handles. A window move is conducted 
 * iff the mouse is pressed <I><B>in</B> the window</I>, and then 
 * dragged (except in the hotspot area, of course). Be sure to leave
 * some space in the window for the window itself, i.e. don't fill the
 * window with components that grab mouse events.
 * <P>
 * The Windows managed by this class, should use lightweight components,
 * at least in the area of the hotspot. If not, the peer component 
 * (e.g. a Panel) could grab the mouse event.
 *
 * @author  Jan-Henrik Haukeland &lt;hauk@tildeslash.com&gt;
 * @version $Id: WindowManager.java,v 1.1 2001/08/13 12:04:15 hauk Exp $
 */
public final class WindowManager
    implements MouseListener, MouseMotionListener
{

  /**
   * Window hotspot width and height. The spot is located in 
   * the South East corner of the window
   */
  public static final int[] RESIZE_SPOT= {10,10};


  /** WindowManager instance. */
  private static WindowManager self;

  /** Window resize track point */
  private Point trackPoint;

  /** True if the mouse is over a resize hotspot */
  private boolean isResize;


  
  /** 
   * Constructs a window manager. 
   */
  private WindowManager() 
  {
  }

  
  /**
   * Returns the instance of the window manager singleton.
   */
  public static WindowManager getInstance() 
  {
    if ( self == null )
	self= new WindowManager();
    
    return self;
  }


  
  /**
   * Register listeners for a window.
   *
   * @param w Window to register for.
   */
  public static void register(Window w) 
  {
    w.addMouseMotionListener(getInstance());
    w.addMouseListener(getInstance());
  }



  /**
   * Remove listeners for a Window.
   *
   * @param w Window to deregister for.
   */
  public static void deregister(Window w) 
  {
    w.removeMouseMotionListener(getInstance());
    w.removeMouseListener(getInstance());
  }



  /** 
   * Sets trackPoint if the left mouse button was pressed.
   *
   * @param e The mouse event.  
   */
  public void mousePressed(MouseEvent e)
  { 
    // if ( e.getModifiers() == InputEvent.BUTTON1_MASK ) {

    // We accept all buttons since not all JVM's report correct
    // button mask, on all OS's (jdk1.1.7B is broken on Solaris)
    {
      Window w= (Window)e.getSource();
      
      w.toFront();
      
      if ( ! isResize )
      {
	setMoveCursor(w); 
      }
      
      trackPoint= translateToScreen(e);
    }
  }

  

  /** 
   * Resize or reposition the window to a new size or a new location 
   * based on the difference between trackPoint and the current mouse 
   * coordinates. Updates trackPoint to the new coordinates.  
   */
  public void mouseDragged(MouseEvent e)
  { 

    if ( trackPoint != null ) {

      Window w= (Window)e.getSource();
      
      Point oldPoint= trackPoint;
      
      trackPoint= translateToScreen(e);
      
      int xdelta= (trackPoint.x - oldPoint.x);
      int ydelta= (trackPoint.y - oldPoint.y);
      
      if ( isResize ) {

	int width=  w.getSize().width +  xdelta;
	int height= w.getSize().height + ydelta;
	if ( (width > 0) && (height > 0) ) {
	  w.setSize(width,height); 
	  //  w.validate(); // FIXME: repaints to often
	}
	
      } else { // got window move

	// Hack to keep the mouse cursor type
	setMoveCursor(w); 

	int x= w.getLocationOnScreen().x + xdelta;
	int y= w.getLocationOnScreen().y + ydelta;

	w.setLocation(x,y);

      }
    }
    
  }


  
  /**
   * Notify the layout manager in case we did a resize.
   */
  public void mouseReleased(MouseEvent e)
  { 
    trackPoint= null;

    Window w= (Window)e.getSource();
    
    if ( isResize )
    {
      w.validate(); 
    }
    
    setDefaultCursor(w);

  }



  /**
   * Change the mouse cursor if it's over a resize hotspot
   */
  public void mouseMoved(MouseEvent e)
  {
    setMouseMovedCursor(e);
  }


  
  /**
   * Left empty.
   */
  public void mouseClicked(MouseEvent e) 
  {
  }



  /**
   * Left empty.
   */
  public void mouseEntered(MouseEvent e)
  {
  }


  
  /**
   * Left empty.
   */
  public void mouseExited(MouseEvent e)
  {
  }




  // ----------------------------------------------------------------- //




  /** 
   * Translates a mouse events x/y cordinates to
   * screen coordinates
   *
   * @param e The mouse event.  
   */
  private Point translateToScreen(MouseEvent e)
  {
    Point screen= ((Window)e.getSource()).getLocationOnScreen();
    screen.x += e.getX();
    screen.y += e.getY();
    return screen;
  }



  /**
   * Change the mouse cursor to the resize type if it's over the 
   * hotspot, else set it to default 
   */
  private void setMouseMovedCursor(MouseEvent e)
  {
    Window    w= (Window)e.getSource();
    Dimension d= w.getSize();
    Point     p= e.getPoint();
    
    if((p.x + RESIZE_SPOT[0] > d.width) && (p.y + RESIZE_SPOT[1] > d.height)) 
    {
      isResize= true;
      setResizeCursor(w);
      return;
    }
    
    isResize= false;
    setDefaultCursor(w);
  }



  private void setResizeCursor(Window w)
  {
    setCursor(w, Cursor.SE_RESIZE_CURSOR);
  }

  private void setDefaultCursor(Window w)
  {
    setCursor(w, Cursor.DEFAULT_CURSOR);
  }

  private void setMoveCursor(Window w)
  {
    setCursor(w, Cursor.MOVE_CURSOR);
  }

  private void setCursor(Window w, int type)
  {
    w.setCursor(Cursor.getPredefinedCursor(type));
  }

}




