package iiuf.swing;

import java.awt.Container;
import java.awt.Component;
import java.awt.MenuComponent;
import java.awt.AWTEvent;
import java.awt.EventQueue;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.event.ActionListener;
import java.awt.event.ItemListener;
import java.lang.reflect.Method;
import javax.swing.JComponent;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.JTable;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JToolTip;
import javax.swing.Action;
import javax.swing.table.TableColumn;
import javax.swing.tree.ExpandVetoException;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeModelEvent;

import iiuf.awt.Awt;
import iiuf.util.AsyncAccelerator;
import iiuf.util.AsyncInvocation;

/**
   Swing utilities, intended be similar to iiuf.awt.Awt.<p>
   
   (c) 2000, 2001, IIUF, DIUF<p>
   
   @author $Author: ohitz $
   @version $Name:  $ $Revision: 1.1 $
   @see iiuf.awt.Awt
*/
public class Swing {
  
  public static JComponent newJComponent() {
    return new JComponent() {};
  }

  private static final Component emptyCmp = Awt.newComponent();
  public static BufferedImage iconToImage(Icon icon) {
    BufferedImage result = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_4BYTE_ABGR);
    icon.paintIcon(emptyCmp, result.getGraphics(), 0, 0);
    return result;
  }
  
  public static JCheckBox newCheckBox(String label, ItemListener listener) {
    JCheckBox result = new JCheckBox(label) {
	public JToolTip createToolTip() {
	  MultiLineToolTip result = new MultiLineToolTip();
	  result.setComponent(this);
	  return result;
	}
      };
    result.addItemListener(listener);
    return result;
  }

  public static JButton newButton(String label, ActionListener listener) {
    return newButton(label, label, listener);
  }
  
  public static JButton newButton(Icon icon, ActionListener listener) {
    return newButton(null, null, icon, listener);
  }

  public static JButton newButton(String label, Icon icon, ActionListener listener) {
    return newButton(label, label, icon, listener);
  }
  
  public static JButton newButton(String label, String command, ActionListener listener) {
    return newButton(label, command, null, listener);
  }
  
  public static JButton newButton(String label, String command, Icon icon, ActionListener listener) {
    JButton result = new JButton(label, icon) {
	public JToolTip createToolTip() {
	  MultiLineToolTip result = new MultiLineToolTip();
	  result.setComponent(this);
	  return result;
	}
      };
    result.addActionListener(listener);
    if(command != null)
      result.setActionCommand(command);
    return result;
  }
  
  public static JMenuItem newMenuItem(String label, ActionListener listener) {
    return newMenuItem(label, listener, null);
  }
  
  public static JRadioButtonMenuItem newRadioButtonMenuItem(String label, ButtonGroup bg, ActionListener listener) {
    return newRadioButtonMenuItem(label, bg, listener, null);
  }
  
  public static JCheckBoxMenuItem newCheckBoxMenuItem(String label, ActionListener listener) {
    return newCheckBoxMenuItem(label, listener, null, false);
  }

  public static JCheckBoxMenuItem newCheckBoxMenuItem(String label, ActionListener listener, boolean state) {
    return newCheckBoxMenuItem(label, listener, null, state);
  }
  
  public static JMenuItem newMenuItem(String label, ActionListener listener, KeyStroke ks) {
    JMenuItem result = new JMenuItem(label) {
	public JToolTip createToolTip() {
	  MultiLineToolTip result = new MultiLineToolTip();
	  result.setComponent(this);
	  return result;
	}
      };
    result.addActionListener(listener);
    if(ks != null) result.setAccelerator(ks);
    return result;
  }
  
  public static JMenuItem newMenuItem(Action action, KeyStroke ks) {
    JMenuItem result = new JMenuItem(action) {
	public JToolTip createToolTip() {
	  MultiLineToolTip result = new MultiLineToolTip();
	  result.setComponent(this);
	  return result;
	}
      };
    if(ks != null) result.setAccelerator(ks);
    return result;
  }
  
  public static JRadioButtonMenuItem newRadioButtonMenuItem(String label, ButtonGroup bg, ActionListener listener, KeyStroke ks) {
    JRadioButtonMenuItem result = new JRadioButtonMenuItem(label) {
	public JToolTip createToolTip() {
	  MultiLineToolTip result = new MultiLineToolTip();
	  result.setComponent(this);
	  return result;
	}
      };
    bg.add(result);
    if(bg.getSelection() == null)
      bg.setSelected(result.getModel(), true);
    result.addActionListener(listener);
    if(ks != null) result.setAccelerator(ks);
    return result;
  }
  
  public static JCheckBoxMenuItem newCheckBoxMenuItem(String label, ActionListener listener, KeyStroke ks) {
    return newCheckBoxMenuItem(label, listener, ks, false);
  }
  
  public static JCheckBoxMenuItem newCheckBoxMenuItem(String label, ActionListener listener, KeyStroke ks, boolean state) {
    JCheckBoxMenuItem result = new JCheckBoxMenuItem(label, state) {
	public JToolTip createToolTip() {
	  MultiLineToolTip result = new MultiLineToolTip();
	  result.setComponent(this);
	  return result;
	}
      };
    result.addActionListener(listener);
    if(ks != null) result.setAccelerator(ks);
    return result;
  }
  
  /*
   * Creates a new EventDispatchThread to dispatch events from. This
   * method returns when stopModal is invoked.
   */
  public static void startModal(javax.swing.JInternalFrame f) {
    synchronized(f) {
      /* Since all input will be blocked until this dialog is dismissed,
       * make sure its parent containers are visible first (this component
       * is tested below).  This is necessary for JApplets, because
       * because an applet normally isn't made visible until after its
       * start() method returns -- if this method is called from start(),
       * the applet will appear to hang while an invisible modal frame
       * waits for input.
       */
      
      if (f.isVisible() && !f.isShowing()) {
	Container parent = f.getParent();
	while (parent != null) {
	  if (parent.isVisible() == false) {
	    parent.setVisible(true);
	  }
	  parent = parent.getParent();
	}
      }

      try {
	if (SwingUtilities.isEventDispatchThread()) {
	  EventQueue theQueue = f.getToolkit().getSystemEventQueue();
	  while (f.isVisible()) {
	    // This is essentially the body of EventDispatchThread
	    AWTEvent event = theQueue.getNextEvent();
	    Object src = event.getSource();
	    // can't call theQueue.dispatchEvent, so I pasted it's body here
	    /*if (event instanceof ActiveEvent) {
	      ((ActiveEvent) event).dispatch();
	      } else */ if (src instanceof Component) {
		((Component) src).dispatchEvent(event);
	      } else if (src instanceof MenuComponent) {
		((MenuComponent) src).dispatchEvent(event);
	      } else {
		System.err.println("unable to dispatch event: " + event);
	      }
	  }
	} else
	  while (f.isVisible())
	    f.wait();
      } catch(InterruptedException e){}
    }
  }

  /*
   * Stops the event dispatching loop created by a previous call to
   * <code>startModal</code>.
   */
  public static void stopModal(javax.swing.JInternalFrame f) {
    synchronized(f) {
      f.notifyAll();
      f.getDesktopPane().remove(f);
    }
  }
  
  public static void setWidth(TableColumn column, int width) {
    column.setPreferredWidth(width);   
    column.setMinWidth(width);
    column.setMaxWidth(width);
  }

  // -------------------- async stuff
  
  private static boolean check(Method m, String method, Class argType) {
    return 
      m.getName().equals(method) && 
      argType == m.getParameterTypes()[0] &&
      m.getParameterTypes().length == 1;
  }
  
  static {
    Awt.addAsyncAccelerator(new AsyncAccelerator() {
	public void handle(AsyncInvocation.Invocation i, AsyncInvocation.Result result) {
	  if(i.object instanceof ListSelectionListener) {
	    ListSelectionListener ll = (ListSelectionListener)i.object;
	    if(check(i.method, "valueChanged", ListSelectionEvent.class)) ll.valueChanged((ListSelectionEvent)i.args[0]);
	    else throw new IllegalArgumentException("Unknown ListSelectionListener method:" + i.method);
	    result.set(null);
	  }
	  
	  else if(i.object instanceof TreeModelListener) {
	    TreeModelListener tl = (TreeModelListener)i.object;
	    if(check(i.method, "treeNodesChanged", TreeModelEvent.class)) 
	      tl.treeNodesChanged((TreeModelEvent)i.args[0]);
	    else if(check(i.method, "treeNodesInserted", TreeModelEvent.class))
	      tl.treeNodesInserted((TreeModelEvent)i.args[0]);
	    else if(check(i.method, "treeNodesRemoved", TreeModelEvent.class))
	      tl.treeNodesRemoved((TreeModelEvent)i.args[0]);
	    else if(check(i.method, "treeStructureChanged", TreeModelEvent.class))
	      tl.treeStructureChanged((TreeModelEvent)i.args[0]);
	    else throw new IllegalArgumentException("Unknown TreeModelListener method:" + i.method);
	    result.set(null);
	  }
	  
	  else if(i.object instanceof TreeWillExpandListener) {
	    TreeWillExpandListener tl = (TreeWillExpandListener)i.object;
	    try {
	      if(check(i.method, "treeWillCollapse", TreeExpansionEvent.class))
		tl.treeWillCollapse((TreeExpansionEvent)i.args[0]);
	      else if(check(i.method, "treeWillExpand", TreeExpansionEvent.class))
		tl.treeWillExpand((TreeExpansionEvent)i.args[0]);
	      else throw new IllegalArgumentException("Unknown TreeWillExpandListener method:" + i.method);
	      result.set(null);
	    } catch(ExpandVetoException ex) {result.setException(ex);}
	  }
	}
      });
  }
  
  private static ListSelectionListener tmpListSelectionListener;
  public static synchronized ListSelectionListener asyncWrapper(ListSelectionListener listener_) {
    tmpListSelectionListener = listener_;
    
    return new ListSelectionListener() {
	ListSelectionListener listener = tmpListSelectionListener;
	
	public void valueChanged(ListSelectionEvent e) {Awt.invokeAsync(listener, "valueChanged", ListSelectionEvent.class, e);}
      };
  }

  private static TreeModelListener tmpTreeModelListener;
  public static synchronized TreeModelListener asyncWrapper(TreeModelListener listener_) {
    tmpTreeModelListener = listener_;
    
    return new TreeModelListener() {
	TreeModelListener listener = tmpTreeModelListener;
	
	public void treeNodesChanged(TreeModelEvent e) {
	  Awt.invokeAsync(listener, "treeNodesChanged", TreeModelEvent.class, e);}
	public void treeNodesInserted(TreeModelEvent e) {
	  Awt.invokeAsync(listener, "treeNodesInserted", TreeModelEvent.class, e);}
	public void treeNodesRemoved(TreeModelEvent e) {
	  Awt.invokeAsync(listener, "treeNodesRemoved", TreeModelEvent.class, e);}
	public void treeStructureChanged(TreeModelEvent e) {
	  Awt.invokeAsync(listener, "treeStructureChanged", TreeModelEvent.class, e);}
      };
  }

  private static TreeWillExpandListener tmpTreeWillExpandListener;
  public static synchronized TreeWillExpandListener asyncWrapper(TreeWillExpandListener listener_) {
    tmpTreeWillExpandListener = listener_;
    
    return new TreeWillExpandListener() {
	TreeWillExpandListener listener = tmpTreeWillExpandListener;
	
	public void treeWillCollapse(TreeExpansionEvent e){
	  Awt.invokeAsync(listener, "treeWillCollapse", TreeExpansionEvent.class, e);}
	public void treeWillExpand(TreeExpansionEvent e) {
	  Awt.invokeAsync(listener, "treeWillExpand", TreeExpansionEvent.class, e);}
      };
  }
  
}

/*
  $Log: Swing.java,v $
  Revision 1.1  2002/07/11 12:09:52  ohitz
  Initial checkin

  Revision 1.13  2001/03/30 17:33:25  schubige
  modified beat soundlet

  Revision 1.12  2001/03/02 17:51:16  schubige
  Enhanced sourcewatch and worked on soundium properties panel

  Revision 1.11  2001/02/17 09:54:21  schubige
  moved graph stuff to iiuf.swing.graph, started work on rotatable GraphNodeComponents

  Revision 1.10  2001/02/14 17:25:37  schubige
  implemented resizing, select all and key-shortcuts for graph panel

  Revision 1.9  2001/02/12 17:50:05  schubige
  still working on soundium gui

  Revision 1.8  2001/02/09 17:34:16  schubige
  working on soundium

  Revision 1.7  2001/01/14 13:21:14  schubige
  Win NT update

  Revision 1.6  2001/01/04 16:28:39  schubige
  Header update for 2001 and DIUF

  Revision 1.5  2000/12/18 12:39:09  schubige
  Added ports to iiuf.util.graph

  Revision 1.4  2000/11/20 17:36:57  schubige
  tinja project ide

  Revision 1.3  2000/11/09 07:48:44  schubige
  early checkin for DCJava

  Revision 1.2  2000/10/03 08:39:39  schubige
  Added tree view and contect menu stuff

  Revision 1.1  2000/08/17 16:22:15  schubige
  Swing cleanup & TreeView added

  Revision 1.1  2000/07/28 12:07:58  schubige
  Graph stuff update
  
*/