/*
 * 作成日: 2007/06/03
 */
package net.sourceforge.swingx;

import java.awt.Component;
import java.awt.Container;
import java.awt.Event;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JMenuItem;
import javax.swing.JPasswordField;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.JTextComponent;
import javax.swing.undo.UndoManager;




public class SxUtilities {
	static Graphics2D image_g2 = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR).createGraphics(); 
    /**
     * 文字列を表示する領域を返す.
     * テキストは空文字はダメです.
     */
	public static Rectangle2D.Float getTextRectangle(Graphics2D g2, String text, Font font) {
		
		if (g2 == null) g2 = image_g2;
		assert text != null && text.length() > 0: text;
		// フォントのサイズ計算
        FontRenderContext frc = g2.getFontRenderContext();
        TextLayout tl = new TextLayout(text, font, frc);
        return new Rectangle2D.Float(0, -tl.getAscent(), 
        		tl.getAdvance(), tl.getAscent()+tl.getDescent());
	}	

	/**
	 * ポップアップメニューにメニューを追加します.
	 * 別のメニューを追加したい場合はこのメソッドを使います。
	 * デフォルトのメニューのみでよいのであれば、addPopupMenuMouseListener(JTextComponent)が便利です。
	 * ActionMapから取得する方法以外に、DefaultEditorKit.CutActionなどを作成する方法もある。
	 */
	public static void addMenuItems(JTextComponent comp, JPopupMenu popup) {
		assert comp != null;
		assert popup != null;
		ActionMap map = comp.getActionMap();
		boolean isEditable = comp.isEditable();
		if (comp instanceof JPasswordField) {
			popup.add(createMenuItem(map.get("paste-from-clipboard"), "貼り付け(P)", KeyEvent.VK_P, isEditable));
			popup.addSeparator();
			popup.add(createMenuItem(map.get("select-all"), "すべて選択(A)", KeyEvent.VK_A, true));
		} else {
			Action undo = map.get("Undo");
			Action redo = map.get("Redo");
			if (undo != null || redo != null) {
				if (undo != null) 
					popup.add(createMenuItem(undo, "元に戻す(U)", KeyEvent.VK_U, isEditable));
				if (redo != null)
					popup.add(createMenuItem(redo, "やり直し(R)", KeyEvent.VK_R, isEditable));
				popup.addSeparator();
			}
			popup.add(createMenuItem(map.get("cut-to-clipboard"), "切り取り(T)", KeyEvent.VK_T, isEditable));
			popup.add(createMenuItem(map.get("copy-to-clipboard"), "コピー(C)", KeyEvent.VK_C, true));
			popup.add(createMenuItem(map.get("paste-from-clipboard"), "貼り付け(P)", KeyEvent.VK_P, isEditable));
			popup.addSeparator();
			popup.add(createMenuItem(map.get("select-all"), "すべて選択(A)", KeyEvent.VK_A, true));
		}
	}

	private static JMenuItem createMenuItem(Action action, String text,
			int mnemonic, boolean enable) {
		assert action != null;
		assert text != null;

		JMenuItem menu = new JMenuItem(action);
		menu.setText(text);
		menu.setMnemonic(mnemonic);
		menu.setEnabled(enable);
		KeyStroke ks = (KeyStroke) action.getValue(Action.ACCELERATOR_KEY);
		if (ks != null) {
			menu.setAccelerator(ks);
		}
		return menu;
	}

	/**
	 * コンポーネントに対してデフォルトのポップアップメニューを表示させるようにする.
	 * マウスリスナーを追加し、その中でポップアップメニューを表示する処理を行っています。
	 * デフォルト以外のメニューを追加したい場合は、addMenuItems(JTextComponent, JPopupMenu)を 使ってください。
	 * 
	 */
	public static void addPopupMenuMouseListener(JTextComponent comp) {
		assert comp != null;
		comp.addMouseListener(new TextComponentMouseListener(comp));
	}

	static class TextComponentMouseListener extends MouseAdapter {
		final JTextComponent comp;

		TextComponentMouseListener(JTextComponent comp) {
			assert comp != null;
			this.comp = comp;
		}
		public void mousePressed(MouseEvent ev) {
			if (ev.isPopupTrigger()) {
				showPopupMenu(ev);
			}
		}
		public void mouseReleased(MouseEvent ev) {
			if (ev.isPopupTrigger()) {
				showPopupMenu(ev);
			}
		}
		private void showPopupMenu(MouseEvent ev) {
			JPopupMenu popup = new JPopupMenu();
			addMenuItems(comp, popup);
			popup.show(comp, ev.getX(), ev.getY());
		}
	}
	public static void setUndoRedoAction(JTextComponent comp) {
		SxUndoableEditorListener listener = new SxUndoableEditorListener();
		comp.getDocument().addUndoableEditListener(listener);
		ActionMap amap = comp.getActionMap();
		amap.put("Undo", listener.undoAction);
		amap.put("Redo", listener.redoAction);
		InputMap imap = comp.getInputMap();
		imap.put(KeyStroke.getKeyStroke('Z', Event.CTRL_MASK), "Undo");
		imap.put(KeyStroke.getKeyStroke('Y', Event.CTRL_MASK), "Redo");
	}
	private static class SxUndoableEditorListener implements UndoableEditListener {
		UndoManager undoManager = new UndoManager();
		RedoAction redoAction = new RedoAction();
		UndoLastAction undoAction = new UndoLastAction();
		
		public void undoableEditHappened(UndoableEditEvent e) {
			undoManager.addEdit(e.getEdit());
			undoAction.update();
			redoAction.update();
		}
		private class RedoAction extends AbstractAction {
			public RedoAction() {
				super("Redo");
				update();
			}
			public void actionPerformed(ActionEvent e) {
				undoManager.redo();
				undoAction.update();
				update();
			}
			public void update() {
				boolean canRedo = undoManager.canRedo();
				if (canRedo) {
					setEnabled(true);
					putValue(Action.NAME, undoManager.getRedoPresentationName());
				} else {
					setEnabled(false);
					putValue(Action.NAME, "Redo");
				}
			}
		}
		private class UndoLastAction extends AbstractAction {
			public UndoLastAction() {
				super("Undo");
				update();
			}
			public void actionPerformed(ActionEvent e) {
				undoManager.undo();
				redoAction.update();
				update();
			}
			public void update() {
				boolean canUndo = undoManager.canUndo();
				if (canUndo) {
					setEnabled(true);
					putValue(Action.NAME, undoManager.getUndoPresentationName());
				} else {
					setEnabled(false);
					putValue(Action.NAME, "Undo");
				}
			}
		}
	}
	
	
	/** 指定された名前のコンポーネントを返す。 */
	public static Component getComponent(Component parent, String name) {
		assert parent != null;
		assert name != null;
		//System.out.println(parent.getName());
		if (name.equals(parent.getName())) return parent;
		if (parent instanceof Container) {
			Container c = (Container) parent;
			for (int i=0; i<c.getComponentCount(); i++) {
				Component ret = getComponent(c.getComponent(i), name);
				if (ret != null) return ret;
			}
		}
		return null;
	}
//	public static BufferedImage createBufferedImage(Image image, ImageObserver io, int imageType) {
//		int w = image.getWidth(io);
//		int h = image.getHeight(io);
//		BufferedImage bi = new BufferedImage(w, h, imageType);
//		Graphics g = bi.getGraphics();
//		g.drawImage(image, 0, 0, io);
//		return bi;
//	}

}
