/* $Id: GSHTMLEditor.java 429 2011-10-17 01:21:38Z minao $ */
/*
GNU Lesser General Public License

EkitCore - Base Java Swing HTML Editor & Viewer Class (Core)
Copyright (C) 2000 Howard Kistler

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package com.hexidec.ekit;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Array;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultComboBoxModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.ChangedCharSetException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.Keymap;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.PlainDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import javax.swing.text.Highlighter;
import javax.swing.text.DefaultHighlighter.DefaultHighlightPainter;

import smart_gs.connection.ConnectionCreator;
import smart_gs.logical.SmartDocument;
import smart_gs.logical.TextSegment;
import smart_gs.smleditor.swingui.GSEditorFrame;
import smart_gs.smleditor.swingui.GSEditorSourcePopupMenu;
import smart_gs.spread_selection.FontUtil;
import smart_gs.spread_selection.SpaceUtil;
import smart_gs.swingui.WorkspaceWindow;
import smart_gs.util.StringUtils;
import sml_editor.swingui.GSHTMLEditorTextPane;

import com.hexidec.ekit.action.CreateHyperlink;
import com.hexidec.ekit.action.ListAutomationAction;
import com.hexidec.ekit.action.RedoAction;
import com.hexidec.ekit.action.SetFontFamilyAction;
import com.hexidec.ekit.action.UndoAction;
import com.hexidec.ekit.component.ExtendedCaret;
import com.hexidec.ekit.component.ExtendedHTMLDocument;
import com.hexidec.ekit.component.ExtendedHTMLEditorKit;
import com.hexidec.ekit.component.HTMLUtilities;
import com.hexidec.ekit.component.JButtonNoFocus;
import com.hexidec.ekit.component.JComboBoxNoFocus;
import com.hexidec.ekit.component.MutableFilter;
import com.hexidec.ekit.component.SimpleInfoDialog;
import com.hexidec.ekit.component.UnicodeDialog;
import com.hexidec.ekit.thirdparty.print.DocumentRenderer;

/**
 * EkitCore Main application class for editing and saving HTML in a Java text
 * component
 * 
 * @author Howard Kistler
 * @version 1.1
 * 
 * REQUIREMENTS Java 2 (JDK 1.3 or 1.4) Swing Library
 */

public class GSHTMLEditor extends JPanel implements ActionListener,
		KeyListener, FocusListener, DocumentListener, MouseMotionListener, PasteListener {

	protected SmartDocument document;

	/* Components */
	private GSHTMLEditorTextPane jtpMain;
	private ExtendedHTMLEditorKit htmlKit;
	private ExtendedHTMLDocument htmlDoc;
	private StyleSheet styleSheet;
	private JTextArea jtpSource;
	private JToolBar jToolBar;
	private CardLayout cardLayout;
	private JPanel cardPanel;
	private boolean sourceWindowActive = false;
//	When keepHighlights = true, highlights are not removed but accumulated.
	private boolean keepHighlights = false;

	private JButtonNoFocus jbtnBold;
	private JButtonNoFocus jbtnItalic;
	private JButtonNoFocus jbtnUnderline;

//	TODO 2011.08.24 ver.0.8では諦める。highlightがうまくいかない。 shayashi
//	private JButtonNoFocus jbtnUList;
//	private JButtonNoFocus jbtnOList;

	private JButtonNoFocus jbtnAlignLeft;
	private JButtonNoFocus jbtnAlignCenter;
	private JButtonNoFocus jbtnAlignRight;
	private JButtonNoFocus jbtnUnicode;

	private JComboBoxNoFocus jcmbFontSelector;
	private JComboBoxNoFocus jcmbFontSizeSelector;
	private JComboBoxNoFocus jcmbColorSelector;

	private HTMLUtilities htmlUtilities = new HTMLUtilities(this);

	/* Actions */
	private StyledEditorKit.BoldAction actionFontBold;
	private StyledEditorKit.ItalicAction actionFontItalic;
	private StyledEditorKit.UnderlineAction actionFontUnderline;
	private ListAutomationAction actionListUnordered;
	private ListAutomationAction actionListOrdered;
	private StyledEditorKit.AlignmentAction actionAlignLeft;
	private StyledEditorKit.AlignmentAction actionAlignCenter;
	private StyledEditorKit.AlignmentAction actionAlignRight;
	private StyledEditorKit.AlignmentAction actionAlignJustified;
	private SetFontFamilyAction setFontFamilyAction;

	// protected UndoManager undoMngr;
	protected GSHTMLUndoManager undoMngr;
	protected UndoAction undoAction;
	protected RedoAction redoAction;

	/* Menus */
	private JMenuBar jMenuBar;
	private JMenu jMenuDebug;

	private JCheckBoxMenuItem jcbmiViewToolbar;

	// Menu & Tool Key Arrays
	private final String menuDialog = "..."; /*
												 * text to append to a MenuItem
												 * label when menu item opens a
												 * dialog
												 */

	// System Clipboard Settings
	private java.awt.datatransfer.Clipboard sysClipboard;
	private SecurityManager secManager;

	private boolean exclusiveEdit = true;

	private int indent = 0;
	private final int indentStep = 4;

	// File extensions for MutableFilter
	private final String[] extsHTML = { "html", "htm", "shtml" };

	protected GSEditorFrame parent;

	protected CreateHyperlink createHyperlink;

	private String currentFontName = "";
	private String currentFontSize = "-1";

	protected String[] colorHTML = { "#000000", "#0000FF", "#00FF00",
			"#00FFFF", "#FF0000", "#FF00FF", "#FFFF00", "#FFFFFF" };
	protected HashMap fontsizeMap = new HashMap();
	protected HashMap fontcolorMap = new HashMap();
	private Frame frameHandler;

	protected FontUtil fontUtilColor;
	protected FontUtil fontUtilSize;
	protected boolean tagdelete = false;
	protected boolean tagdeleteprocess = false;
	protected int tagdeletepos = 0;
	protected boolean fulltype = false;
	protected boolean fulltypego = false;

	protected boolean initialized = false;

	protected GSHTMLEditorSearchActionListner searchActionListner;
	protected GSHTMLEditorFoucsActionListner  foucsActionListner;
	
	public GSHTMLEditor(boolean showViewSource, boolean editModeExclusive,
			boolean debugMode) {
		this(null, showViewSource, editModeExclusive, debugMode);
	}

	public GSHTMLEditor(String source, boolean showViewSource,
			boolean editModeExclusive, boolean debugMode) {
		super();
		exclusiveEdit = editModeExclusive;
		frameHandler = new Frame();
		// Determine if system clipboard is available
		secManager = System.getSecurityManager();
		if (secManager != null) {
			try {
				secManager.checkSystemClipboardAccess();
				sysClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
			} catch (SecurityException se) {
				sysClipboard = null;
			}
		}
		sysClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

		/* Set up the undo features */
		// undoMngr = new UndoManager();
		undoMngr = new GSHTMLUndoManager();
		undoAction = new UndoAction(this);
		redoAction = new RedoAction(this);

		/* Create the editor kit, document, and stylesheet */
		jtpMain = new GSHTMLEditorTextPane(undoAction,redoAction);
		htmlKit = new ExtendedHTMLEditorKit();
		htmlDoc = (ExtendedHTMLDocument) (htmlKit.createDefaultDocument());
		styleSheet = htmlDoc.getStyleSheet();
		htmlKit.setDefaultCursor(new Cursor(Cursor.TEXT_CURSOR));
		jtpMain.setCursor(new Cursor(Cursor.TEXT_CURSOR));

		//jtpMain.setCaret(new ExtendedCaret());
		
		/* Set up the text pane */
		jtpMain.setEditorKit(htmlKit);
		jtpMain.setDocument(htmlDoc);
		jtpMain.setMargin(new Insets(4, 4, 4, 4));
		jtpMain.addKeyListener(this);
		jtpMain.addFocusListener(this);
		jtpMain.addMouseMotionListener(this);
		jtpMain.setPasteListener(this);
		// jtpMain.setDragEnabled(true); // this causes an error in older Java
		// versions

		/* Create the source text area */
		jtpSource = new JTextArea();
		jtpSource.setText(jtpMain.getText());
		jtpSource.setBackground(new Color(212, 212, 212));
		jtpSource.setSelectionColor(new Color(255, 192, 192));
		jtpSource.setMargin(new Insets(4, 4, 4, 4));
		jtpSource.getDocument().addDocumentListener(this);
		jtpSource.addFocusListener(this);
		jtpSource.setCursor(new Cursor(Cursor.TEXT_CURSOR));
		// 折り返し設定
		jtpSource.setLineWrap(true);
		//jtpMain.setText(jtpSource.getText());
		/* Add CaretListener for tracking caret location events */
		jtpMain.addCaretListener(new CaretListener() {
			public void caretUpdate(CaretEvent ce) {
				handleCaretPositionChange(ce);
			}
		});
		//
		JPopupMenu sourcePopupMenu = new GSEditorSourcePopupMenu(jtpSource);
		jtpSource.setComponentPopupMenu(sourcePopupMenu);
		
		//jtpMain.setCaretPosition(0);
		jtpMain.getDocument().addDocumentListener(this);

		Keymap km = jtpMain.getKeymap();
		KeyStroke ksctrlz = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
				Event.CTRL_MASK);
		km.addActionForKeyStroke(ksctrlz, undoAction);

		KeyStroke ksctrly = KeyStroke.getKeyStroke(KeyEvent.VK_Y,
				Event.CTRL_MASK);
		km.addActionForKeyStroke(ksctrly, redoAction);

		KeyStroke ksctrlv = KeyStroke.getKeyStroke(KeyEvent.VK_V,
				Event.CTRL_MASK);
		km.addActionForKeyStroke(ksctrlv, new AbstractAction(){
			public void actionPerformed(ActionEvent e) {
				jtpMain.paste(true);
			}
		});

		// スタイルシート
		String sStyleSheet = "css.txt";
		if (sStyleSheet != null) {
			File defCSS = new File(sStyleSheet);
			if (defCSS.exists()) {
				try {
					openStyleSheet(defCSS);
				} catch (Exception e) {
					logException("Exception in preloading CSS stylesheet", e);
				}
			}
		}

		/* Collect the actions that the JTextPane is naturally aware of */
		Hashtable actions = new Hashtable();
		Action[] actionsArray = jtpMain.getActions();
		for (int i = 0; i < actionsArray.length; i++) {
			Action a = actionsArray[i];
			actions.put(a.getValue(Action.NAME), a);
		}

		/* Create shared actions */
		actionFontBold = new StyledEditorKit.BoldAction();
		actionFontItalic = new StyledEditorKit.ItalicAction();
		actionFontUnderline = new StyledEditorKit.UnderlineAction();
		actionListUnordered   = new ListAutomationAction(this, "Unordered List", HTML.Tag.UL);
		actionListOrdered     = new ListAutomationAction(this, "Ordered List", HTML.Tag.OL);
		actionAlignLeft = new StyledEditorKit.AlignmentAction("Align Left*",StyleConstants.ALIGN_LEFT);
		actionAlignCenter = new StyledEditorKit.AlignmentAction("Align Center*", StyleConstants.ALIGN_CENTER);
		actionAlignRight = new StyledEditorKit.AlignmentAction("Align Right*",StyleConstants.ALIGN_RIGHT);
		actionAlignJustified = new StyledEditorKit.AlignmentAction(	"Align Justified*", StyleConstants.ALIGN_JUSTIFIED);
		createHyperlink = new CreateHyperlink(this, "Hyperlink*" + menuDialog,
				HTML.Tag.A);

		/* EDIT Menu */
		if (sysClipboard != null) {
			// System Clipboard versions of menu commands
			/*
			 * JMenuItem jmiCut = new JMenuItem("Cut*");
			 * jmiCut.setActionCommand("textcut");
			 * jmiCut.addActionListener(this);
			 * jmiCut.setAccelerator(KeyStroke.getKeyStroke('X',
			 * KeyEvent.CTRL_MASK, false)); jMenuEdit.add(jmiCut);
			 * 
			 * JMenuItem jmiCopy = new JMenuItem("Copy*");
			 * jmiCopy.setActionCommand("textcopy");
			 * jmiCopy.addActionListener(this);
			 * jmiCopy.setAccelerator(KeyStroke.getKeyStroke('C',
			 * KeyEvent.CTRL_MASK, false)); jMenuEdit.add(jmiCopy);
			 * 
			 * JMenuItem jmiPaste = new JMenuItem("Paste*");
			 * jmiPaste.setActionCommand("textpaste");
			 * jmiPaste.addActionListener(this);
			 * jmiPaste.setAccelerator(KeyStroke.getKeyStroke('V',
			 * KeyEvent.CTRL_MASK, false)); jMenuEdit.add(jmiPaste);
			 */
		}
		/* DEBUG Menu */
		jMenuDebug = new JMenu("Debug*");
		JMenuItem jmiDesc = new JMenuItem("Describe Doc*");
		jmiDesc.setActionCommand("describe");
		jmiDesc.addActionListener(this);
		jMenuDebug.add(jmiDesc);

		JMenuItem jmiDescCSS = new JMenuItem("Describe CSS*");
		jmiDescCSS.setActionCommand("describecss");
		jmiDescCSS.addActionListener(this);
		jMenuDebug.add(jmiDescCSS);

		JMenuItem jmiTag = new JMenuItem("What Tags?*");
		jmiTag.setActionCommand("whattags");
		jmiTag.addActionListener(this);
		jMenuDebug.add(jmiTag);

		/* Create menubar and add menus */
		jMenuBar = new JMenuBar();
		// jMenuBar.add(jMenuView);
		if (debugMode) {
			// jMenuBar.add(jMenuDebug);
		} else {
			// jMenuBar.add(jMenuDebug);
		}

		/* Create toolbar tool objects */
		jbtnBold = new JButtonNoFocus(actionFontBold);
		jbtnBold.setIcon(getEkitIcon("Bold"));
		jbtnBold.setText(null);
		jbtnBold.setToolTipText("Bold*");
		jbtnItalic = new JButtonNoFocus(actionFontItalic);
		jbtnItalic.setIcon(getEkitIcon("Italic"));
		jbtnItalic.setText(null);
		jbtnItalic.setToolTipText("Italic*");
		jbtnUnderline = new JButtonNoFocus(actionFontUnderline);
		jbtnUnderline.setIcon(getEkitIcon("Underline"));
		jbtnUnderline.setText(null);
		jbtnUnderline.setToolTipText("Underline*");
//		jbtnUList = new JButtonNoFocus(actionListUnordered);
//		jbtnUList.setIcon(getEkitIcon("UList"));
//		jbtnUList.setText(null);
//		jbtnUList.setToolTipText("Unordered List");
//		jbtnOList = new JButtonNoFocus(actionListOrdered);
//		jbtnOList.setIcon(getEkitIcon("OList"));
//		jbtnOList.setText(null);
//		jbtnOList.setToolTipText("Ordered List");
		jbtnAlignLeft = new JButtonNoFocus(actionAlignLeft);
		jbtnAlignLeft.setIcon(getEkitIcon("AlignLeft"));
		jbtnAlignLeft.setText(null);
		jbtnAlignLeft.setToolTipText("Align Left*");
		jbtnAlignCenter = new JButtonNoFocus(actionAlignCenter);
		jbtnAlignCenter.setIcon(getEkitIcon("AlignCenter"));
		jbtnAlignCenter.setText(null);
		jbtnAlignCenter.setToolTipText("Align Center*");
		jbtnAlignRight = new JButtonNoFocus(actionAlignRight);
		jbtnAlignRight.setIcon(getEkitIcon("AlignRight"));
		jbtnAlignRight.setText(null);
		jbtnAlignRight.setToolTipText("Align Right*");
		jbtnUnicode = new JButtonNoFocus();
		jbtnUnicode.setActionCommand("insertunicode");
		jbtnUnicode.addActionListener(this);
		jbtnUnicode.setIcon(getEkitIcon("Unicode"));
		jbtnUnicode.setText(null);
		jbtnUnicode.setToolTipText("Insert Unicode Characters*");

		String[] fonts = java.awt.GraphicsEnvironment
				.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
		Vector vcFontnames = new Vector(fonts.length + 1);
		vcFontnames.add("(default)*");
		for (int i = 0; i < fonts.length; i++) {
			vcFontnames.add(fonts[i]);
		}
		Collections.sort(vcFontnames);
		jcmbFontSelector = new JComboBoxNoFocus(vcFontnames);
		jcmbFontSelector.setMaximumSize(jcmbFontSelector.getPreferredSize());
		setFontFamilyAction = new SetFontFamilyAction(this,
				"[EKITFONTSELECTOR]");
		jcmbFontSelector.setAction(setFontFamilyAction);

		/* フォントサイズ選択用コンボボックス */
		jcmbFontSizeSelector = new JComboBoxNoFocus((new String[] { "8", "10",
				"12", "14", "18", "24", "32" }));
		jcmbFontSizeSelector.setMaximumSize(jcmbFontSizeSelector
				.getPreferredSize());
		jcmbFontSizeSelector.addActionListener(this);
		jcmbFontSizeSelector.setActionCommand("fontsizes");
		jcmbFontSizeSelector.setSelectedIndex(4);

		fontsizeMap.put("1", "8");
		fontsizeMap.put("2", "10");
		fontsizeMap.put("3", "12");
		fontsizeMap.put("4", "14");
		fontsizeMap.put("-", "18");
		fontsizeMap.put("6", "24");
		fontsizeMap.put("7", "32");

		/* 前景色用コンボボックス */
		DefaultComboBoxModel colorModel = new DefaultComboBoxModel();
		for (int i = 0; i < 8; i++) {
			/* 色つきのラベルを作成する */
			StringBuffer sb = new StringBuffer();
			sb.append("<html><font color=\"");
			sb.append(colorHTML[i]);
			fontcolorMap.put(colorHTML[i].toLowerCase(), new Integer(i));
			sb.append("\">■</font></html>");

			colorModel.addElement(new String(sb));
		}
		jcmbColorSelector = new JComboBoxNoFocus(colorModel);
		jcmbColorSelector.setMaximumSize(jcmbColorSelector.getPreferredSize());
		jcmbColorSelector.addActionListener(this);
		jcmbColorSelector.setActionCommand("fontcolors");
		jcmbColorSelector.setSelectedIndex(0);

		jToolBar = new JToolBar(JToolBar.HORIZONTAL);
		jToolBar.add(jbtnBold);
		jToolBar.add(jbtnItalic);
		jToolBar.add(jbtnUnderline);

//		jToolBar.add(jbtnAlignLeft);
//		jToolBar.add(jbtnAlignCenter);
//		jToolBar.add(jbtnAlignRight);
		jToolBar.addSeparator();
//		jToolBar.add(jbtnUList);
//		jToolBar.add(jbtnOList);
		jToolBar.addSeparator();
		jToolBar.add(jbtnUnicode);
		jToolBar.addSeparator();
		jToolBar.add(jcmbFontSelector);
		jToolBar.add(jcmbColorSelector);
		jToolBar.addSeparator();
		jToolBar.add(jcmbFontSizeSelector);

		jToolBar.setVisible(true);
		jToolBar.setFloatable(false);
		jMenuBar.add(jToolBar);
		// initializeSingleToolbar(toolbarSeq);

		/* Create the scroll area for the text pane */
		JScrollPane jspViewport = new JScrollPane(jtpMain);
		jspViewport
				.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		jspViewport.setPreferredSize(new Dimension(400, 400));
		jspViewport.setMinimumSize(new Dimension(32, 32));
		// 20080910 hashimoto

		/* Add the components to the app */
		cardLayout = new CardLayout();
		cardPanel = new JPanel();
		JScrollPane scroll1 = new JScrollPane(jtpMain);
		scroll1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		cardPanel.setLayout(cardLayout);
		cardPanel.add(scroll1, "edit");
		JScrollPane scroll2 = new JScrollPane(jtpSource);
		scroll2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		cardPanel.add(scroll2, "source");
		this.setLayout(new BorderLayout());
		this.add(cardPanel, BorderLayout.CENTER);
		this.add(jMenuBar, BorderLayout.NORTH);

		if (source != null && !source.trim().equals("")) {
			source = source.replaceAll(" ", "&#160;");
			//jtpMain.setText("<html><head></head><body>" + source
			//		+ "</body></html>");
			jtpMain.setText(source);
		}else{
			//jtpMain.setText("");
		}
		jtpMain.setCaretPosition(0);

		// スタイルシートを読み込むとundoが記録されるので
		// 一旦リセット
		undoMngr = new GSHTMLUndoManager();
		learn();

		initialized = true;

		fontUtilColor = new FontUtil(this, "color");
		fontUtilSize = new FontUtil(this, "font-size");
		
	}

	
	public boolean isKeepHighlights() {
		return keepHighlights;
	}
	
	public void setKeepHighlights(boolean keepHighlights) {
		this.keepHighlights = keepHighlights;
	}
	
	/* ActionListener method */
	public void actionPerformed(ActionEvent ae) {
		try {
			String command = ae.getActionCommand();
			if (command.equals("textcut")) {
				if (isSourceWindowActive() && jtpSource.hasFocus()) {
					jtpSource.cut();
				} else {
					jtpMain.cut();
				}
			} else if (command.equals("textcopy")) {
				if (isSourceWindowActive() && jtpSource.hasFocus()) {
					jtpSource.copy();
				} else {
					jtpMain.copy();
				}
				try {
					System.out.println(sysClipboard
							.getData(DataFlavor.stringFlavor));
				} catch (UnsupportedFlavorException e) {
					// TODO 自動生成された catch ブロック
					e.printStackTrace();
				}
			} else if (command.equals("textpaste")) {
				if (isSourceWindowActive() && jtpSource.hasFocus()) {
					jtpSource.paste();
				} else {
					jtpMain.paste();
				}
			} else if (command.equals("insertunicode")) {
				insertUnicode(UnicodeDialog.UNICODE_BASE);
			} else if (command.equals("print")) {
				DocumentRenderer dr = new DocumentRenderer();
				dr.print(htmlDoc);

			} else if (command.equals("describe")) {
				System.out.println("------------DOCUMENT------------");
				System.out
						.println("Content Type : " + jtpMain.getContentType());
				System.out.println("Editor Kit   : " + jtpMain.getEditorKit());
				System.out.println("Doc Tree     :");
				System.out.println("");
				describeDocument(jtpMain.getStyledDocument());
				System.out.println("--------------------------------");
				System.out.println("");
			} else if (command.equals("describecss")) {
				System.out.println("-----------STYLESHEET-----------");
				System.out.println("Stylesheet Rules");
				Enumeration rules = styleSheet.getStyleNames();
				while (rules.hasMoreElements()) {
					String ruleName = (String) (rules.nextElement());
					Style styleRule = styleSheet.getStyle(ruleName);
					System.out.println(styleRule.toString());
				}
				System.out.println("--------------------------------");
				System.out.println("");
			} else if (command.equals("whattags")) {
				System.out.println("Caret Position : "
						+ jtpMain.getCaretPosition());
				AttributeSet attribSet = jtpMain.getCharacterAttributes();
				Enumeration attribs = attribSet.getAttributeNames();
				System.out.println("Attributes     : ");
				while (attribs.hasMoreElements()) {
					String attribName = attribs.nextElement().toString();
					System.out.println("                 " + attribName + " | "
							+ attribSet.getAttribute(attribName));
				}
			} else if (command.equals("toggletoolbar")) {
				jToolBar.setVisible(jcbmiViewToolbar.isSelected());
			} else if (command.equals("viewsource")) {
				toggleSourceWindow();
			} else if (command.equals("insertbreak")) {
				insertBreak();
			} else if (command.equals("insertnbsp")) {
				insertNonbreakingSpace();
			} else if (command.equals("fontcolors")) {
				/* 前景色 */
				int start = getGSHTMLEditorTextPane().getSelectionStart();
				int end = getGSHTMLEditorTextPane().getSelectionEnd();
				if (start == end) {
					return;
				}
				int col = jcmbColorSelector.getSelectedIndex();
				if (col == 0) {
					if (fontUtilColor.check(start, end)) {
						while (fontUtilColor.check(start, end)) {
							fontUtilColor.releasMakup();
						}
						return;
					}
					return;
				}
				int b = (col % 2) * 255;
				int g = ((col / 2) % 2) * 255;
				int r = ((col / 4) % 2) * 255;
				new StyledEditorKit.ForegroundAction("fontcolors", new Color(r,
						g, b)).actionPerformed(ae);
			} else if (command.equals("fontsizes")) {
				int start = getGSHTMLEditorTextPane().getSelectionStart();
				int end = getGSHTMLEditorTextPane().getSelectionEnd();
				if (start == end) {
					return;
				}
				MutableAttributeSet attribSet = jtpMain.getInputAttributes();
				String fontsizeStr = jcmbFontSizeSelector.getSelectedItem()
						.toString();
				if (fontsizeStr.equals("18")) {
					if (fontUtilSize.check(start, end)) {
						while (fontUtilSize.check(start, end)) {
							fontUtilSize.releasMakup();
						}
						return;
					}
					return;
				}
				/* フォントサイズ変更 */
				int fontSize = 0;
				try {
					fontSize = Integer.parseInt(jcmbFontSizeSelector
							.getSelectedItem().toString());
				} catch (NumberFormatException ex) {
					return;
				}
				// attribSet.getAttribute(new
				// StyleConstants.FontConstants("font-size"));

				/*
				 * boolean bold = (StyleConstants.isBold(attr)) ? false : true;
				 * SimpleAttributeSet sas = new SimpleAttributeSet();
				 * StyleConstants.setBold(sas, bold);
				 * setCharacterAttributes(editor, sas, false);
				 */

				new StyledEditorKit.FontSizeAction("fontsize", fontSize)
						.actionPerformed(ae);
			}
		} catch (IOException ioe) {
			logException("IOException in actionPerformed method", ioe);
			SimpleInfoDialog sidAbout = new SimpleInfoDialog(getFrame(),
					"Error*", true, "IO Exception occurred.*",
					SimpleInfoDialog.ERROR);
		} catch (BadLocationException ble) {
			logException("BadLocationException in actionPerformed method", ble);
			SimpleInfoDialog sidAbout = new SimpleInfoDialog(getFrame(),
					"Error*", true, "Bad Location Exception occurred.*",
					SimpleInfoDialog.ERROR);
		} catch (NumberFormatException nfe) {
			logException("NumberFormatException in actionPerformed method", nfe);
			SimpleInfoDialog sidAbout = new SimpleInfoDialog(getFrame(),
					"Error*", true, "NumberFormat Exception occurred.*",
					SimpleInfoDialog.ERROR);
		} catch (RuntimeException re) {
			logException("RuntimeException in actionPerformed method", re);
			SimpleInfoDialog sidAbout = new SimpleInfoDialog(getFrame(),
					"Error*", true, "Runtime Exception occurred.*",
					SimpleInfoDialog.ERROR);
		}
	}

	/* KeyListener methods */
	public void keyTyped(KeyEvent ke) {
		Element elem;
		String selectedText;
		int pos = this.getCaretPosition();
		int repos = -1;

		if (ke.getKeyChar() == KeyEvent.VK_BACK_SPACE) {
			try {
				if (pos > 0) {
					if ((selectedText = jtpMain.getSelectedText()) != null) {
						htmlUtilities.delete();
						return;
					} else {
						int sOffset = htmlDoc.getParagraphElement(pos)
								.getStartOffset();
						if (sOffset == jtpMain.getSelectionStart()) {
							boolean content = true;
							if (htmlUtilities.checkParentsTag(HTML.Tag.LI)) {
								elem = htmlUtilities.getListItemParent();
								content = false;
								int so = elem.getStartOffset();
								int eo = elem.getEndOffset();
								if (so + 1 < eo) {
									char[] temp = jtpMain.getText(so, eo - so)
											.toCharArray();
									for (int i = 0; i < temp.length; i++) {
										if (!(new Character(temp[i]))
												.isWhitespace(temp[i])) {
											content = true;
										}
									}
								}
								if (!content) {
									Element listElement = elem
											.getParentElement();
									htmlUtilities.removeTag(elem, true);
									this.setCaretPosition(sOffset - 1);
									return;
								} else {
									jtpMain.setCaretPosition(jtpMain
											.getCaretPosition() - 1);
									jtpMain.moveCaretPosition(jtpMain
											.getCaretPosition() - 2);
									jtpMain.replaceSelection("");
									return;
								}
							} else if (htmlUtilities
									.checkParentsTag(HTML.Tag.TABLE)) {
								jtpMain.setCaretPosition(jtpMain
										.getCaretPosition() - 1);
								ke.consume();
								return;
							}
						}
						jtpMain.replaceSelection("");
						return;
					}
				}
			} catch (BadLocationException ble) {
				logException("BadLocationException in keyTyped method", ble);
				SimpleInfoDialog sidAbout = new SimpleInfoDialog(getFrame(),
						"Error*", true, "Bad Location Exception occurred.*",
						SimpleInfoDialog.ERROR);
			} catch (IOException ioe) {
				logException("IOException in keyTyped method", ioe);
				SimpleInfoDialog sidAbout = new SimpleInfoDialog(getFrame(),
						"Error*", true, "IO Exception occurred.*",
						SimpleInfoDialog.ERROR);
			}
		} else if (ke.getKeyChar() == KeyEvent.VK_ENTER) {
			try {
				elem = htmlUtilities.getListItemParent();
				if(elem != null && this.getCaretPosition() + 1 == elem.getEndOffset())
				{
					insertListStyle(elem);
					this.setCaretPosition(pos - repos);
				}else{
					insertBreak();
					ke.consume();
				}
			} catch (BadLocationException ble) {
				logException("BadLocationException in keyTyped method", ble);
				SimpleInfoDialog sidAbout = new SimpleInfoDialog(getFrame(),
						"Error*", true, "Bad Location Exception occurred.*",
						SimpleInfoDialog.ERROR);
			} catch (IOException ioe) {
				logException("IOException in keyTyped method", ioe);
				SimpleInfoDialog sidAbout = new SimpleInfoDialog(getFrame(),
						"Error*", true, "IO Exception occurred.*",
						SimpleInfoDialog.ERROR);
			}
		} else if (ke.getKeyChar() == KeyEvent.VK_SPACE) {
			try {
				int mod = ke.getModifiersEx();
				if ((mod & InputEvent.CTRL_DOWN_MASK) != 0) {
					tagdelete = true;
					tagdeletepos = jtpMain.getCaretPosition();
					return;
				} else {
					insertNonbreakingSpace();
				}
				ke.consume();
			} catch (BadLocationException ble) {
				logException("BadLocationException in keyTyped method", ble);
				SimpleInfoDialog sidAbout = new SimpleInfoDialog(getFrame(),
						"Error*", true, "Bad Location Exception occurred.*",
						SimpleInfoDialog.ERROR);
			} catch (IOException ioe) {
				logException("IOException in keyTyped method", ioe);
				SimpleInfoDialog sidAbout = new SimpleInfoDialog(getFrame(),
						"Error*", true, "IO Exception occurred.*",
						SimpleInfoDialog.ERROR);
			}
		} else if (ke.getKeyChar() == KeyEvent.VK_DELETE) {
			try {
				if( getCaretPosition() == htmlDoc.getLength() && htmlDoc.getLength() > 1 ){

					stopDocumentListener();
					int p = getCaretPosition();
					jtpMain.setText(getSubText("body"));
					setCaretPosition(p);
					
					startDocumentListener();
					
					ke.consume();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		tagdeleteprocess = true;
	}
	public void insertListStyle(Element element)
	throws BadLocationException,IOException
	{
		if(element.getParentElement().getName() == "ol")
		{
			actionListOrdered.actionPerformed(new ActionEvent(new Object(), 0, "newListPoint"));
		}
		else
		{
			actionListUnordered.actionPerformed(new ActionEvent(new Object(), 0, "newListPoint"));
		}
	}
	public void keyPressed(KeyEvent e) {
		int keycode = e.getKeyCode();
		String presskey = e.getKeyText(keycode);
		int mod = e.getModifiersEx();
		if ((mod & InputEvent.CTRL_DOWN_MASK) != 0) {
			if (presskey.toLowerCase().equals("z") || presskey.toLowerCase().equals("Z")) {
				// undoAction.undo();
			}
		} else if ((mod & InputEvent.CTRL_DOWN_MASK) != 0) {
			if (presskey.toLowerCase().equals("z") || presskey.toLowerCase().equals("Z")) {
				// redoAction.redo();
			}
		}
	}

	public void keyReleased(KeyEvent e) {
		int keycode = e.getKeyCode();
		String presskey = e.getKeyText(keycode);
		int mod = e.getModifiersEx();
		if ((mod & InputEvent.CTRL_DOWN_MASK) != 0) {
			if (presskey.toLowerCase().equals("z") || presskey.toLowerCase().equals("Z")) {
				return;
			}
		}
		if ((mod & InputEvent.CTRL_DOWN_MASK) != 0) {
			if (presskey.toLowerCase().equals("y") || presskey.toLowerCase().equals("Y")) {
				return;
			}
		}
		if ((mod & InputEvent.CTRL_DOWN_MASK) != 0) {
			if (presskey.toLowerCase().equals("v") || presskey.toLowerCase().equals("V")) {
				return;
			}
		}
		if ((mod & InputEvent.CTRL_DOWN_MASK) != 0) {
			if (presskey.toLowerCase().equals("t") || presskey.toLowerCase().equals("T")) {
				if(searchActionListner != null){
					searchActionListner.actionPerformed();
				}
			}
		}
		if (keycode == KeyEvent.VK_CONTROL) {
			return;
		}
		if (e.getKeyChar() == KeyEvent.VK_SPACE
				&& (mod & InputEvent.CTRL_DOWN_MASK) != 0) {
			e.consume();
			return;
		}
		// 全角入力Off
		if (keycode == KeyEvent.VK_FULL_WIDTH) {
			fulltype = false;
			fulltypego = false;
			return;
		}
		// 全角入力On
		if (keycode == KeyEvent.VK_HALF_WIDTH
				|| keycode == KeyEvent.VK_ALPHANUMERIC) {
			fulltype = true;
			fulltypego = false;
			return;
		}
		// 全角入力を完了判定
		if (keycode == 10) {
			if (fulltype) {
				fulltypego = true;
			} else {
				fulltypego = false;
			}
		}
		// アスキー入力の場合はそこからスタート
		// 全角入力は全角入力完了判定処理後
		if (tagdelete) {
			if (tagdeleteprocess) {
				if (fulltype) {
					if (!fulltypego) {
						return;
					}
				}
				int caretPos = jtpMain.getCaretPosition();
				SpaceUtil spaceUtil = new SpaceUtil(this);
				spaceUtil.remove(tagdeletepos, caretPos);
				tagdelete = false;
			}
		}
		fulltypego = false;
		tagdeleteprocess = false;
		learn();
	}

	/* FocusListener methods */
	public void focusGained(FocusEvent fe) {
		if (fe.getSource() == jtpMain) {
			setFormattersActive(true);
			sourceWindowActive = false;
		} else if (fe.getSource() == jtpSource) {
			setFormattersActive(false);
			sourceWindowActive = true;
		}
		if(foucsActionListner != null){
			foucsActionListner.focusGained();
		}
	}

	public void focusLost(FocusEvent fe) {
		if(foucsActionListner != null){
			foucsActionListner.focusGained();
		}
	}

	/* DocumentListener methods */
	public void changedUpdate(DocumentEvent de) {
		learn();
		handleDocumentChange(de);
	}

	public void insertUpdate(DocumentEvent de) {
		int offset = de.getOffset();
		int length = de.getLength();
		try {
			String text = de.getDocument().getText(offset, length);
			if (text.length() == 1) {
				learn();
			}
		} catch (BadLocationException e1) {
			e1.printStackTrace();
		}
		handleDocumentChange(de);
	}

	public void removeUpdate(DocumentEvent de) {
		handleDocumentChange(de);
	}

	public void handleDocumentChange(DocumentEvent de) {
		if (!exclusiveEdit) {
			if (isSourceWindowActive()) {
				if (de.getDocument() instanceof HTMLDocument
						|| de.getDocument() instanceof ExtendedHTMLDocument) {
					jtpSource.getDocument().removeDocumentListener(this);
					jtpSource.setText(jtpMain.getText());
					jtpSource.getDocument().addDocumentListener(this);
				} else if (de.getDocument() instanceof PlainDocument
						|| de.getDocument() instanceof DefaultStyledDocument) {
					jtpMain.getDocument().removeDocumentListener(this);
					jtpMain.setText(jtpSource.getText());
					jtpMain.getDocument().addDocumentListener(this);
				}
			}
		}
	}

	/**
	 * Method for setting a document as the current document for the text pane
	 * and re-registering the controls and settings for it
	 */
	public void registerDocument(ExtendedHTMLDocument htmlDoc) {
		jtpMain.setDocument(htmlDoc);
		// jtpMain.getDocument().addUndoableEditListener(new
		// CustomUndoableEditListener());
		jtpMain.getDocument().addDocumentListener(this);
		jtpMain.setCaretPosition(0);
		purgeUndos();
	}

	/**
	 * Method for inserting a break (BR) element
	 */
	/*
	 * private void insertBreak() throws IOException, BadLocationException,
	 * RuntimeException { int caretPos = jtpMain.getCaretPosition();
	 * htmlKit.insertHTML(htmlDoc, caretPos, "<BR>", 0, 0, HTML.Tag.BR);
	 * refreshOnUpdate(); jtpMain.setCaretPosition(caretPos); purgeUndos(); }
	 */
	private void insertBreak() throws IOException, BadLocationException,
			RuntimeException {
		int caretPos = jtpMain.getCaretPosition();
		//System.out.println("11:"+caretPos);
		boolean insertTemporary = false;
		if( caretPos == 1 ){
			htmlDoc.insertString(caretPos, "\n", jtpMain.getInputAttributes());
			caretPos++;
			insertTemporary = true;
		}
		
		htmlKit.insertHTML(htmlDoc, caretPos, "<BR>", 0, 0, HTML.Tag.BR);

		if( insertTemporary ){
			htmlDoc.remove(1, 1);
			caretPos--;
		}
		
		jtpMain.setCaretPosition(caretPos + 1);
		
		int caretPos2 = jtpMain.getCaretPosition();
		//System.out.println("22:"+caretPos2);
	}

	/**
	 * Method for opening the Unicode dialog
	 */
	private void insertUnicode(int index) throws IOException,
			BadLocationException, RuntimeException {
		UnicodeDialog unicodeInput = new UnicodeDialog(this,
				"Unicode Character Insert*", false, index);
	}

	/**
	 * Method for inserting Unicode characters via the UnicodeDialog class
	 */
	public void insertUnicodeChar(String sChar) throws IOException,
			BadLocationException, RuntimeException {
		int caretPos = jtpMain.getCaretPosition();
		if (sChar != null) {
			htmlDoc.insertString(caretPos, sChar, jtpMain.getInputAttributes());
			jtpMain.setCaretPosition(caretPos + 1);
		}
	}

	/**
	 * Method for inserting a non-breaking space (&nbsp;)
	 */
	private void insertNonbreakingSpace() throws IOException,
			BadLocationException, RuntimeException {
		int caretPos = jtpMain.getCaretPosition();
		htmlDoc.insertString(caretPos, "\240", jtpMain.getInputAttributes());
		jtpMain.setCaretPosition(caretPos + 1);
	}

	/**
	 * Method that handles initial list insertion and deletion
	 */
	public void manageListElement(Element element) {
		Element h = htmlUtilities.getListItemParent();
		Element listElement = h.getParentElement();
		if (h != null) {
			htmlUtilities.removeTag(h, true);
		}
	}

	/**
	 * Method for saving text as an HTML fragment
	 */
	private void writeOutFragment(HTMLDocument doc, String containingTag)
			throws IOException, BadLocationException {
		File whatFile = getFileFromChooser(".", JFileChooser.SAVE_DIALOG,
				extsHTML, "HTML Files*");
		if (whatFile != null) {
			FileWriter fw = new FileWriter(whatFile);
			// Element eleBody = locateElementInDocument((StyledDocument)doc,
			// containingTag);
			// htmlKit.write(fw, doc, eleBody.getStartOffset(),
			// eleBody.getEndOffset());
			String docTextCase = jtpSource.getText().toLowerCase();
			int tagStart = docTextCase.indexOf("<"
					+ containingTag.toLowerCase());
			int tagStartClose = docTextCase.indexOf(">", tagStart) + 1;
			String closeTag = "</" + containingTag.toLowerCase() + ">";
			int tagEndOpen = docTextCase.indexOf(closeTag);
			if (tagStartClose < 0) {
				tagStartClose = 0;
			}
			if (tagEndOpen < 0 || tagEndOpen > docTextCase.length()) {
				tagEndOpen = docTextCase.length();
			}
			String bodyText = jtpSource.getText().substring(tagStartClose,
					tagEndOpen);
			fw.write(bodyText, 0, bodyText.length());
			fw.flush();
			fw.close();
		}
		refreshOnUpdate();
	}

	/**
	 * Method to invoke loading HTML into the app
	 */
	private void openDocument(File whatFile) throws IOException,
			BadLocationException {
		if (whatFile == null) {
			whatFile = getFileFromChooser(".", JFileChooser.OPEN_DIALOG,
					extsHTML, "HTML Files*");
		}
		if (whatFile != null) {
			try {
				loadDocument(whatFile, null);
			} catch (ChangedCharSetException ccse) {
				String charsetType = ccse.getCharSetSpec().toLowerCase();
				int pos = charsetType.indexOf("charset");
				if (pos == -1) {
					throw ccse;
				}
				while (pos < charsetType.length()
						&& charsetType.charAt(pos) != '=') {
					pos++;
				}
				pos++; // Places file cursor past the equals sign (=)
				String whatEncoding = charsetType.substring(pos).trim();
				loadDocument(whatFile, whatEncoding);
			}
		}
		refreshOnUpdate();
	}

	/**
	 * Method for loading HTML document
	 */
	public void loadDocument(File whatFile) throws IOException,
			BadLocationException {
		try {
			loadDocument(whatFile, null);
		} catch (ChangedCharSetException ccse) {
			String charsetType = ccse.getCharSetSpec().toLowerCase();
			int pos = charsetType.indexOf("charset");
			if (pos == -1) {
				throw ccse;
			}
			while (pos < charsetType.length() && charsetType.charAt(pos) != '=') {
				pos++;
			}
			pos++; // Places file cursor past the equals sign (=)
			String whatEncoding = charsetType.substring(pos).trim();
			loadDocument(whatFile, whatEncoding);
		}
		refreshOnUpdate();
	}

	/**
	 * Method for loading HTML document into the app, including document
	 * encoding setting
	 */
	private void loadDocument(File whatFile, String whatEncoding)
			throws IOException, BadLocationException {
		Reader r = null;
		htmlDoc = (ExtendedHTMLDocument) (htmlKit.createDefaultDocument());
		htmlDoc.putProperty("com.hexidec.ekit.docsource", whatFile.toString());
		try {
			if (whatEncoding == null) {
				r = new InputStreamReader(new FileInputStream(whatFile));
			} else {
				r = new InputStreamReader(new FileInputStream(whatFile),
						whatEncoding);
				htmlDoc
						.putProperty("IgnoreCharsetDirective",
								new Boolean(true));
			}
			htmlKit.read(r, htmlDoc, 0);
			r.close();
			registerDocument(htmlDoc);
			jtpSource.setText(jtpMain.getText());
			// currentFile = whatFile;
		} finally {
			if (r != null) {
				r.close();
			}
		}
	}

	/**
	 * Method for obtaining a File for input/output using a JFileChooser dialog
	 */
	private File getFileFromChooser(String startDir, int dialogType,
			String[] exts, String desc) {
		JFileChooser jfileDialog = new JFileChooser(startDir);
		jfileDialog.setDialogType(dialogType);
		jfileDialog.setFileFilter(new MutableFilter(exts, desc));
		int optionSelected = JFileChooser.CANCEL_OPTION;
		if (dialogType == JFileChooser.OPEN_DIALOG) {
			optionSelected = jfileDialog.showOpenDialog(this);
		} else if (dialogType == JFileChooser.SAVE_DIALOG) {
			optionSelected = jfileDialog.showSaveDialog(this);
		} else // default to an OPEN_DIALOG
		{
			optionSelected = jfileDialog.showOpenDialog(this);
		}
		if (optionSelected == JFileChooser.APPROVE_OPTION) {
			return jfileDialog.getSelectedFile();
		}
		return (File) null;
	}

	/**
	 * Method for loading a Stylesheet into the app
	 */
	private void openStyleSheet(File fileCSS) throws IOException {
		if (fileCSS != null) {
			String currDocText = jtpMain.getText();
			htmlDoc = (ExtendedHTMLDocument) (htmlKit.createDefaultDocument());
			styleSheet = htmlDoc.getStyleSheet();
			URL cssUrl = fileCSS.toURL();
			InputStream is = cssUrl.openStream();
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			styleSheet.loadRules(br, cssUrl);
			br.close();

			htmlDoc = new ExtendedHTMLDocument(styleSheet);
			registerDocument(htmlDoc);
			jtpMain.setText(currDocText);
			jtpSource.setText(jtpMain.getText());
		}
		refreshOnUpdate();
	}

	/**
	 * Method for describing the node hierarchy of the document
	 */
	private void describeDocument(StyledDocument doc) {
		Element[] elements = doc.getRootElements();
		for (int i = 0; i < elements.length; i++) {
			indent = indentStep;
			for (int j = 0; j < indent; j++) {
				System.out.print(" ");
			}
			System.out.print(elements[i]);
			traverseElement(elements[i]);
			System.out.println("");
		}
	}

	/**
	 * Traverses nodes for the describing method
	 */
	private void traverseElement(Element element) {
		indent += indentStep;
		for (int i = 0; i < element.getElementCount(); i++) {
			for (int j = 0; j < indent; j++) {
				System.out.print(" ");
			}
			System.out.print(element.getElement(i));
			traverseElement(element.getElement(i));
		}
		indent -= indentStep;
	}

	/**
	 * Convenience method for obtaining the WYSIWYG JTextPane
	 */
	public JTextPane getTextPane() {
		return jtpMain;
	}

	/**
	 * Convenience method for obtaining the Source JTextPane
	 */
	public JTextArea getSourcePane() {
		return jtpSource;
	}

	/**
	 * Convenience method for obtaining the application as a Frame
	 */
	public Frame getFrame() {
		return frameHandler;
	}

	/**
	 * Convenience method for setting the parent Frame
	 */
	public void setFrame(Frame parentFrame) {
		frameHandler = parentFrame;
	}

	/**
	 * Convenience method for obtaining the pre-generated menu bar
	 */
	public JMenuBar getMenuBar() {
		return jMenuBar;
	}

	/**
	 * Convenience method for obtaining the pre-generated toolbar
	 */
	public JToolBar getToolBar(boolean isShowing) {
		if (jToolBar != null) {
			jcbmiViewToolbar.setState(isShowing);
			return jToolBar;
		}
		return (JToolBar) null;
	}

	/**
	 * Convenience method for activating/deactivating formatting commands
	 * depending on the active editing pane
	 */
	private void setFormattersActive(boolean state) {
		actionFontBold.setEnabled(state);
		actionFontItalic.setEnabled(state);
		actionFontUnderline.setEnabled(state);
		actionAlignLeft.setEnabled(state);
		actionAlignCenter.setEnabled(state);
		actionAlignRight.setEnabled(state);
		actionAlignJustified.setEnabled(state);
		jcmbFontSelector.setEnabled(state);
	}

	/**
	 * Convenience method for obtaining the document text
	 */
	public String getDocumentText() {
		if (isSourceWindowActive()) {
			return jtpSource.getText();
		} else {
			return jtpMain.getText();
		}
	}

	/**
	 * Convenience method for obtaining the document text contained within a tag
	 * pair
	 */
	public String getDocumentSubText(String tagBlock) {
		return getSubText(tagBlock);
	}

	/**
	 * Method for extracting the text within a tag
	 */
	private String getSubText(String containingTag) {
		jtpSource.setText(jtpMain.getText());
		String docTextCase = jtpSource.getText().toLowerCase();
		int tagStart = docTextCase.indexOf("<" + containingTag.toLowerCase());
		int tagStartClose = docTextCase.indexOf(">", tagStart) + 1;
		String closeTag = "</" + containingTag.toLowerCase() + ">";
		int tagEndOpen = docTextCase.indexOf(closeTag);
		if (tagStartClose < 0) {
			tagStartClose = 0;
		}
		if (tagEndOpen < 0 || tagEndOpen > docTextCase.length()) {
			tagEndOpen = docTextCase.length();
		}
		return jtpSource.getText().substring(tagStartClose, tagEndOpen);
	}

	/**
	 * Convenience method for obtaining the document text contained within the
	 * BODY tags (a common request)
	 */
	public String getDocumentBody() {
		return getSubText("body");
	}

	/**
	 * Convenience method for setting the document text
	 */
	public void setDocumentText(String sText) {
		jtpMain.setText(sText);
		((HTMLEditorKit) (jtpMain.getEditorKit())).setDefaultCursor(new Cursor(
				Cursor.TEXT_CURSOR));
		jtpSource.setText(jtpMain.getText());
	}

	/**
	 * Convenience method for setting the source document
	 */
	public void setSourceDocument(StyledDocument sDoc) {
		jtpSource.getDocument().removeDocumentListener(this);
		jtpSource.setDocument(sDoc);
		jtpSource.getDocument().addDocumentListener(this);
		jtpMain.setText(jtpSource.getText());
		((HTMLEditorKit) (jtpMain.getEditorKit())).setDefaultCursor(new Cursor(
				Cursor.TEXT_CURSOR));
	}

	/**
	 * Convenience method for communicating the current font selection to the
	 * CustomAction class
	 */
	public String getFontNameFromSelector() {
		if (jcmbFontSelector == null
				|| jcmbFontSelector.getSelectedItem().equals("(default)*")) {
			return (String) null;
		} else {
			return jcmbFontSelector.getSelectedItem().toString();
		}
	}

	/**
	 * Convenience method for clearing out the UndoManager
	 */
	public void purgeUndos() {
		/*
		 * if(undoMngr != null){ undoMngr.discardAllEdits();
		 * undoAction.updateUndoState(); redoAction.updateRedoState(); }
		 */
	}

	/**
	 * Convenience method for refreshing and displaying changes
	 */
	public void refreshOnUpdate() {
		jtpMain.setText(jtpMain.getText());
		jtpSource.setText(jtpMain.getText());
		// purgeUndos();
		this.repaint();
	}

	/**
	 * Convenience method for fetching icon images from jar file
	 */
	private ImageIcon getEkitIcon(String iconName) {
		URL imageURL = GSHTMLEditor.class.getResource("icons/" + iconName
				+ "HK.png");
		if (imageURL != null) {
			return new ImageIcon(Toolkit.getDefaultToolkit().getImage(imageURL));
		}
		imageURL = GSHTMLEditor.class.getResource("icons/" + iconName
				+ "HK.gif");
		if (imageURL != null) {
			return new ImageIcon(Toolkit.getDefaultToolkit().getImage(imageURL));
		}
		return (ImageIcon) null;
	}

	/**
	 * Convenience method for outputting exceptions
	 */
	private void logException(String internalMessage, Exception e) {
		System.err.println(internalMessage);
		e.printStackTrace(System.err);
	}

	/**
	 * Convenience method for determining if the source window is active
	 */
	private boolean isSourceWindowActive() {
		return sourceWindowActive;
		// return (jspSource != null && jspSource ==
		// jspltDisplay.getRightComponent());
	}

	/**
	 * Method for toggling source window visibility
	 */
	private void toggleSourceWindow() {
		cardLayout.next(cardPanel);
		if (!(isSourceWindowActive())) {
			jtpSource.setText(UnicodeDecode.decode(jtpMain.getText()));
			if (exclusiveEdit) {
				jtpSource.requestFocus();
			}
			sourceWindowActive = true;
		} else {
			//jtpMain.setText(jtpSource.getText()));
			jtpMain.setText(HTMLUtilities.getBodyText(jtpSource.getText()));
			jtpMain.requestFocus();
		}
		this.validate();
	}

	/**
	 * Handles caret tracking and related events, such as displaying the current
	 * style of the text under the caret
	 */
	AtomicBoolean through = new AtomicBoolean(false);

	private void handleCaretPositionChange(CaretEvent ce) {
		if (through.get()) {
			return;
		}
		int caretPos = ce.getDot();
		Element element = htmlDoc.getCharacterElement(caretPos);
		/*
		 * ---- TAG EXPLICATOR CODE -------------------------------------------
		 * javax.swing.text.ElementIterator ei = new
		 * javax.swing.text.ElementIterator(htmlDoc); Element ele; while((ele =
		 * ei.next()) != null) { System.out.println("ELEMENT : " +
		 * ele.getName()); } System.out.println("ELEMENT:" + element.getName());
		 * Element elementParent = element.getParentElement();
		 * System.out.println("ATTRS:"); AttributeSet attribs =
		 * elementParent.getAttributes(); for(Enumeration eAttrs =
		 * attribs.getAttributeNames(); eAttrs.hasMoreElements();) {
		 * System.out.println(" " + eAttrs.nextElement().toString()); }
		 * while(elementParent != null &&
		 * !elementParent.getName().equals("body")) { String parentName =
		 * elementParent.getName(); System.out.println("PARENT:" + parentName);
		 * System.out.println("ATTRS:"); attribs =
		 * elementParent.getAttributes(); for(Enumeration eAttr =
		 * attribs.getAttributeNames(); eAttr.hasMoreElements();) {
		 * System.out.println(" " + eAttr.nextElement().toString()); }
		 * elementParent = elementParent.getParentElement(); } ---- END
		 * -------------------------------------------
		 */
		if (jtpMain.hasFocus()) {
			if (element == null) {
				return;
			}
			Vector vcStyles = new Vector();
			while (element != null) {
				vcStyles.add(element);
				element = element.getParentElement();
			}
			int stylefound = -1;
			int start = getGSHTMLEditorTextPane().getSelectionStart();
			int end = getGSHTMLEditorTextPane().getSelectionEnd();
			// see if current font face is set

			if (jcmbFontSelector != null && jcmbFontSelector.isVisible()) {
				Object activeFontName = (Object) ("(default)*");
				if (start == end) {
					AttributeSet mainAttrs = jtpMain.getCharacterAttributes();
					Enumeration e = mainAttrs.getAttributeNames();
					while (e.hasMoreElements()) {
						Object nexte = e.nextElement();
						if (nexte.toString().toLowerCase().equals("face")
								|| nexte.toString().toLowerCase().equals(
										"font-family")) {
							activeFontName = mainAttrs.getAttribute(nexte);
							break;
						}
					}
				} else {
					Object fontType = setFontFamilyAction.getFontType(start,
							end);
					if (fontType != null) {
						activeFontName = fontType;
					}
				}
				setFontFamilyAction.stop();
				jcmbFontSelector.getModel().setSelectedItem(activeFontName);
				setFontFamilyAction.start();
			}
			if (jcmbFontSizeSelector != null
					&& jcmbFontSizeSelector.isVisible()) {
				Object activeFontName = (Object) ("18");
				if (start == end) {
					AttributeSet mainAttrs = jtpMain.getCharacterAttributes();
					Enumeration e = mainAttrs.getAttributeNames();
					while (e.hasMoreElements()) {
						Object nexte = e.nextElement();
						if (nexte.toString().toLowerCase().equals("font-size")) {
							activeFontName = mainAttrs.getAttribute(nexte);
							activeFontName = fontsizeMap.get(activeFontName
									.toString());
							break;
						}
					}
				} else {
					String selectStr = fontUtilSize.selectValue(start, end);
					if (selectStr != null) {
						if (!selectStr.equals("-")) {
							activeFontName = fontsizeMap.get(selectStr);
						}
					}
				}
				jcmbFontSizeSelector.removeActionListener(this);
				jcmbFontSizeSelector.getModel().setSelectedItem(activeFontName);
				jcmbFontSizeSelector.addActionListener(this);
			}
			if (jcmbColorSelector != null && jcmbFontSizeSelector.isVisible()) {
				AttributeSet mainAttrs = jtpMain.getCharacterAttributes();
				Enumeration e = mainAttrs.getAttributeNames();
				int anIndex = 0;
				if (start == end) {
					while (e.hasMoreElements()) {
						Object nexte = e.nextElement();
						if (nexte.toString().toLowerCase().equals("color")) {
							Object obj = mainAttrs.getAttribute(nexte);
							String color = obj.toString();
							Integer colorInt = (Integer) fontcolorMap
									.get(color);
							if (colorInt != null) {
								anIndex = colorInt.intValue();
							}
							// activeFontName =
							// fontsizeMap.get(activeFontName.toString());
							break;
						}
					}
				} else {
					String selectStr = fontUtilColor.selectValue(start, end);
					if (selectStr != null) {
						Integer colorInt = (Integer) fontcolorMap.get(selectStr
								.toLowerCase());
						if (colorInt != null) {
							anIndex = colorInt.intValue();
						}
					}
				}
				jcmbColorSelector.removeActionListener(this);
				jcmbColorSelector.setSelectedIndex(anIndex);
				jcmbColorSelector.addActionListener(this);
			}
		}
	}

	/**
	 * Utility methods
	 */
	public ExtendedHTMLDocument getExtendedHtmlDoc() {
		return (ExtendedHTMLDocument) htmlDoc;
	}

	public int getCaretPosition() {
		return jtpMain.getCaretPosition();
	}

	public void setCaretPosition(int newPositon) {
		boolean end = true;
		do {
			end = true;
			try {
				jtpMain.setCaretPosition(newPositon);
			} catch (IllegalArgumentException iae) {
				end = false;
				newPositon--;
			}
		} while (!end && newPositon >= 0);
	}

	public void learn() {
		if (initialized) {
			WorkspaceWindow.setUpdated(true);
		}
		undoMngr.add(getSource(),getCaretPosition());
	}

	public void mouseDragged(MouseEvent e) {
		// TODO 自動生成されたメソッド・スタブ

	}

	public void mouseMoved(MouseEvent e) {
		// if(!isSourceWindowActive()){
		boolean isHand = false;
		int pos = jtpMain.viewToModel(e.getPoint());
		Element element = htmlDoc.getCharacterElement(pos);
		element.getDocument();
		AttributeSet attributeSet = element.getAttributes();
		Enumeration enumeration = attributeSet.getAttributeNames();
		while (enumeration.hasMoreElements()) {
			Object keyobj = enumeration.nextElement();
			if (keyobj instanceof HTML.Tag) {
				if (((HTML.Tag) keyobj).toString()
						.equals(HTML.Tag.A.toString())) {
					Object obj = attributeSet.getAttribute(keyobj);
					jtpMain.setCursor(new Cursor(Cursor.HAND_CURSOR));
					if (obj instanceof SimpleAttributeSet) {
						SimpleAttributeSet simpleAttr = (SimpleAttributeSet) obj;
						if (document != null) {
							TextSegment segment = null;
							String url = (String) simpleAttr.getAttribute(HTML.Attribute.HREF);
							if(url.startsWith("http")){
								jtpMain.setToolTipText(url);
							}else{
								try{
									int idInt = new Integer(url);
									segment = document.getSegmentByID(idInt);
								}catch(NumberFormatException ex){}
							}
							if (segment != null) {
								jtpMain.setToolTipText(segment.getURI());
							}
						}
					}
					isHand = true;
				}
			}
			// }
			if (!isHand) {
				jtpMain.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
				jtpMain.setToolTipText("");
			}
			// Element eleSearch = htmlDoc.getCharacterElement(pos);
		}
	}

	public GSHTMLEditorTextPane getGSHTMLEditorTextPane() {
		return jtpMain;
	}

	public String getSource(String containingTag) {
		String docTextCase;
		if (!(isSourceWindowActive())) {
			docTextCase = jtpMain.getText();
		} else {
			docTextCase = jtpSource.getText();
		}
		int tagStart = docTextCase.indexOf("<" + containingTag.toLowerCase());
		int tagStartClose = docTextCase.indexOf(">", tagStart) + 1;
		String closeTag = "</" + containingTag.toLowerCase() + ">";
		int tagEndOpen = docTextCase.indexOf(closeTag);
		if (tagStartClose < 0) {
			tagStartClose = 0;
		}
		if (tagEndOpen < 0 || tagEndOpen > docTextCase.length()) {
			tagEndOpen = docTextCase.length();
		}
		String bodyText;
		if (!(isSourceWindowActive())) {
			bodyText = jtpMain.getText().substring(tagStartClose, tagEndOpen);
		} else {
			bodyText = jtpSource.getText().substring(tagStartClose, tagEndOpen);
		}
		bodyText = bodyText.trim();
		bodyText = bodyText.replaceAll("\r", "");
		bodyText = bodyText.replaceAll("\n", "");
		bodyText = bodyText.replaceAll("\r\n", "");
		bodyText = bodyText.replaceAll("&#160;", " ");
		bodyText = UnicodeDecode.decode(bodyText);
		return bodyText;
	}

	public String getPainText() {
		String src;
		if (!(isSourceWindowActive())) {
			src = jtpMain.getText();
		} else {
			src = jtpSource.getText();
		}
		src = src.replaceAll(" ", "");
		src = src.replaceAll("\r", "");
		src = src.replaceAll("\n", "");
		src = src.replaceAll("\r\n", "");
		src = src.replaceAll("\\</p\\>", "\r\n");
		src = src.replaceAll("\\<br\\>", "\r\n");
		Pattern pattern = Pattern.compile("<.+?>", Pattern.DOTALL);
		Matcher matcher = pattern.matcher(src);
		src = matcher.replaceAll("");
		src = src.replaceAll("&quot;", "\"");
		src = src.replaceAll("&gt;", "<");
		src = src.replaceAll("&lt;", ">");
		src = src.replaceAll("&amp;", "&");
		src = UnicodeDecode.decode(src);
		return src;
	}

/*	public void setSelection(int start,int end){

		if (!(isSourceWindowActive())) {
			jtpMain.setSelectionStart(start);
			jtpMain.setSelectionEnd(end);
		} else {
			jtpSource.setSelectionStart(start);
			jtpSource.setSelectionEnd(end);
		}
	} */
	public void setSelection(int start,int end){
		JTextComponent jtpFindSource;
		int  adjust = 0;
		if (!(isSourceWindowActive())) {
			jtpFindSource = (JTextComponent)jtpMain;
		} else {
			jtpFindSource = (JTextComponent)jtpSource;
			adjust = 1;
		}
		Document baseDocument = jtpFindSource.getDocument();

		AttributeSet attribs = null;
		if(baseDocument instanceof HTMLDocument)
		{
			Element element = ((HTMLDocument)baseDocument).getCharacterElement(start);
			attribs = element.getAttributes();
		}
		if(start < end){
			jtpFindSource.setCaretPosition((start + (end-start)) - adjust);
		}
		jtpFindSource.select(start - adjust, start + (end-start) - adjust );
	}
	public int getSelectionStart(){
		JTextComponent jtpFindSource;
		if (!(isSourceWindowActive())) {
			jtpFindSource = (JTextComponent)jtpMain;
		} else {
			jtpFindSource = (JTextComponent)jtpSource;
		}
		return jtpFindSource.getSelectionStart();

	}
	public int getSelectionEnd(){
		JTextComponent jtpFindSource;
		if (!(isSourceWindowActive())) {
			jtpFindSource = (JTextComponent)jtpMain;
		} else {
			jtpFindSource = (JTextComponent)jtpSource;
		}
		return jtpFindSource.getSelectionEnd();
	}
	
	public void setReplaceSelection(String replaceTerm){
		JTextComponent jtpFindSource;
		if (!(isSourceWindowActive())) {
			jtpFindSource = (JTextComponent)jtpMain;
		} else {
			jtpFindSource = (JTextComponent)jtpSource;
		}
		int start = jtpFindSource.getSelectionStart();
		int end = jtpFindSource.getSelectionEnd();
		if(start != end){
			setReplaceSelection(start,end,replaceTerm);
		}
	}
	public int  setReplaceSelection(int start,int end,String replaceTerm){
		JTextComponent jtpFindSource;
		if (!(isSourceWindowActive())) {
			jtpFindSource = (JTextComponent)jtpMain;
		} else {
			jtpFindSource = (JTextComponent)jtpSource;
		}
		Document baseDocument = jtpFindSource.getDocument();

		AttributeSet attribs = null;
		if(baseDocument instanceof HTMLDocument)
		{
			Element element = ((HTMLDocument)baseDocument).getCharacterElement(start);
			attribs = element.getAttributes();
		}
		try {
			learn();
			baseDocument.remove(start,end-start);
			baseDocument.insertString(start, replaceTerm, attribs);
			jtpFindSource.setCaretPosition(start + replaceTerm.length());
//			jtpFindSource.requestFocus();
			jtpFindSource.select(start, start + replaceTerm.length());
			learn();
			return start + replaceTerm.length();
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		return -1;

	}
	
	/** Method for finding (and optionally replacing) a string in the text
	  */
	public int findText(String findTerm, String replaceTerm, boolean bCaseSenstive, int iOffset,boolean forward)
	{
		// 判定を反転
		bCaseSenstive = !bCaseSenstive;
		boolean issource = false;
		JTextComponent jtpFindSource;
		if(isSourceWindowActive() || jtpSource.hasFocus())
		{
			jtpFindSource = (JTextComponent)jtpSource;
			issource = true;
		}
		else
		{
			jtpFindSource = (JTextComponent)jtpMain;
		}
		int searchPlace = -1;
		try
		{
			Document baseDocument = jtpFindSource.getDocument();
			if(forward){
				searchPlace =
					(bCaseSenstive ?
						baseDocument.getText(0, baseDocument.getLength()).toLowerCase().indexOf(findTerm.toLowerCase(), iOffset):
						baseDocument.getText(0, baseDocument.getLength()).indexOf(findTerm, iOffset)
					);
			}else{
				searchPlace =
					(bCaseSenstive ?
						baseDocument.getText(0, baseDocument.getLength()).toLowerCase().lastIndexOf(findTerm.toLowerCase(), iOffset) :
						baseDocument.getText(0, baseDocument.getLength()).lastIndexOf(findTerm, iOffset)
					);
			}
			if(searchPlace > -1)
			{
				if(replaceTerm != null)
				{
					AttributeSet attribs = null;
					if(baseDocument instanceof HTMLDocument)
					{
						Element element = ((HTMLDocument)baseDocument).getCharacterElement(searchPlace);
						attribs = element.getAttributes();
					}
					learn();
					baseDocument.remove(searchPlace, findTerm.length());
					baseDocument.insertString(searchPlace, replaceTerm, attribs);
					jtpFindSource.setCaretPosition(searchPlace + replaceTerm.length());
//					jtpFindSource.requestFocus();
					jtpFindSource.select(searchPlace, searchPlace + replaceTerm.length());
					learn();
					if(forward){
						return searchPlace + replaceTerm.length() -1;
					}else{
						return searchPlace;
					}
				}
				else
				{
					jtpFindSource.setCaretPosition(searchPlace + findTerm.length());
//					jtpFindSource.requestFocus();
					jtpFindSource.select(searchPlace, searchPlace + findTerm.length());
					if(forward){
						return searchPlace + findTerm.length() -1;
					}else{
						return searchPlace;
					}
				}
			}
		}
		catch(BadLocationException ble)
		{
			ble.printStackTrace();
		}
		return searchPlace;
	}

	
	
	public void createHyperlink(String tagname, String id) {
		createHyperlink.setTagname(tagname, id);
		createHyperlink.actionPerformed(null);
	}

	public SimpleAttributeSet getSelectID(Point point) {
		int pos = jtpMain.viewToModel(point);
		return getSelectIDFromPos(pos);
	}
	public SimpleAttributeSet getSelectIDFromPos(int pos) {
		Element element = htmlDoc.getCharacterElement(pos);
		element.getDocument();
		AttributeSet attributeSet = element.getAttributes();
		Enumeration enumeration = attributeSet.getAttributeNames();
		while (enumeration.hasMoreElements()) {
			Object keyobj = enumeration.nextElement();
			if (keyobj instanceof HTML.Tag) {
				if (((HTML.Tag) keyobj).toString()
						.equals(HTML.Tag.A.toString())) {
					Object obj = attributeSet.getAttribute(keyobj);
					if (obj instanceof SimpleAttributeSet) {
						return (SimpleAttributeSet) obj;
					}
				}
			}
		}
		return null;
	}
	public void changeID(Point point,String url,boolean Dummy) {
		int pos = jtpMain.viewToModel(point);
		Element element = htmlDoc.getCharacterElement(pos);
		//element.getDocument();
		AttributeSet attributeSet = element.getAttributes();
		Enumeration enumeration = attributeSet.getAttributeNames();
		while (enumeration.hasMoreElements()) {
			Object keyobj = enumeration.nextElement();
			if (keyobj instanceof HTML.Tag) {
				if (((HTML.Tag) keyobj).toString()
						.equals(HTML.Tag.A.toString())) {
					Object obj = attributeSet.getAttribute(keyobj);
					if (obj instanceof SimpleAttributeSet) {
						SimpleAttributeSet simpleAttributeSet = (SimpleAttributeSet)obj;
						Enumeration enumeration2 = simpleAttributeSet.getAttributeNames();
						Object deleteObject = null;
						while (enumeration2.hasMoreElements()) {
							Object obj2 = enumeration2.nextElement();
							if(obj2.equals(HTML.Attribute.HREF)){
								deleteObject = obj2;
								break;
							}
						}
						if(deleteObject != null){
							simpleAttributeSet.removeAttribute(deleteObject);
							simpleAttributeSet.addAttribute(deleteObject, url);
						}
					}
				}
			}
		}
	}
	public void setURL(Point point, String url){
		int pos = jtpMain.viewToModel(point);
		Element element = htmlDoc.getCharacterElement(pos);
		AttributeSet attributeSet = element.getAttributes();
		Enumeration enumeration = attributeSet.getAttributeNames();
		while (enumeration.hasMoreElements()) {
			Object keyobj = enumeration.nextElement();
			if (keyobj instanceof HTML.Tag) {
				if (((HTML.Tag) keyobj).toString()
						.equals(HTML.Tag.A.toString())) {
					Object obj = attributeSet.getAttribute(keyobj);
					if (obj instanceof SimpleAttributeSet) {
						SimpleAttributeSet simpleAttributeSet = (SimpleAttributeSet)obj;
						simpleAttributeSet.addAttribute(ConnectionCreator.EXTERNAL_LINK, url);
					}
				}
			}
		}
	}
	public void removeURL(Point point) {
		int pos = jtpMain.viewToModel(point);
		Element element = htmlDoc.getCharacterElement(pos);
		AttributeSet attributeSet = element.getAttributes();
		Enumeration enumeration = attributeSet.getAttributeNames();
		while (enumeration.hasMoreElements()) {
			Object keyobj = enumeration.nextElement();
			if (keyobj instanceof HTML.Tag) {
				if (((HTML.Tag) keyobj).toString()
						.equals(HTML.Tag.A.toString())) {
					Object obj = attributeSet.getAttribute(keyobj);
					if (obj instanceof SimpleAttributeSet) {
						SimpleAttributeSet simpleAttributeSet = (SimpleAttributeSet)obj;
						simpleAttributeSet.removeAttribute(ConnectionCreator.EXTERNAL_LINK);
					}
				}
			}
		}
		
	}

	public String getSelectIDStr(Point point) {
		int pos = jtpMain.viewToModel(point);
		Element element = htmlDoc.getCharacterElement(pos);
		element.getDocument();
		AttributeSet attributeSet = element.getAttributes();
		Enumeration enumeration = attributeSet.getAttributeNames();
		while (enumeration.hasMoreElements()) {
			Object keyobj = enumeration.nextElement();
			if (keyobj instanceof HTML.Tag) {
				if (((HTML.Tag) keyobj).toString()
						.equals(HTML.Tag.A.toString())) {
					Object obj = attributeSet.getAttribute(keyobj);
					if (obj instanceof SimpleAttributeSet) {
						SimpleAttributeSet simpleAttributeSet = (SimpleAttributeSet)obj;
						Enumeration enumeration2 = simpleAttributeSet.getAttributeNames();
						while (enumeration2.hasMoreElements()) {
							Object obj2 = enumeration2.nextElement();
							if(obj2.equals(HTML.Attribute.HREF)){
								Object hrefObj = simpleAttributeSet.getAttribute(obj2);
								return hrefObj.toString();
							}
						}					
					}
				}
			}
		}
		return null;

	}
	//2011/10/16 kukita
	public String getSelectURLStr(Point point) {
		int pos = jtpMain.viewToModel(point);
		Element element = htmlDoc.getCharacterElement(pos);
		element.getDocument();
		AttributeSet attributeSet = element.getAttributes();
		Enumeration enumeration = attributeSet.getAttributeNames();
		while (enumeration.hasMoreElements()) {
			Object keyobj = enumeration.nextElement();
			if (keyobj instanceof HTML.Tag) {
				if (((HTML.Tag) keyobj).toString()
						.equals(HTML.Tag.A.toString())) {
					Object obj = attributeSet.getAttribute(keyobj);
					if (obj instanceof SimpleAttributeSet) {
						SimpleAttributeSet simpleAttributeSet = (SimpleAttributeSet)obj;
						Enumeration enumeration2 = simpleAttributeSet.getAttributeNames();
						while (enumeration2.hasMoreElements()) {
							Object obj2 = enumeration2.nextElement();
							if(obj2.equals(ConnectionCreator.EXTERNAL_LINK)){
								Object obj3 = simpleAttributeSet.getAttribute(obj2);
								return obj3.toString();
							}
						}
					}
				}
			}
		}
		return null;

	}
	public GSHTMLUndoManager getUndoManager() {
		return undoMngr;
	}

	public UndoAction getUndoAction() {
		return undoAction;
	}

	public RedoAction getRedoAction() {
		return redoAction;
	}

//	public void set
	public void stopDocumentListener() {
		jtpSource.getDocument().removeDocumentListener(this);
		jtpMain.getDocument().removeDocumentListener(this);
	}

	public void startDocumentListener() {
		jtpSource.getDocument().addDocumentListener(this);
		jtpMain.getDocument().addDocumentListener(this);
	}

	public void setMenuMode(boolean browse) {
		Component[] comps = this.getComponents();
		if (browse) {
			this.remove(jMenuBar);
		} else {
			if (comps == null) {
				this.add(jMenuBar, BorderLayout.NORTH);
			} else {
				boolean found = false;
				for (int i = 0; i < comps.length; i++) {
					if (comps[i] == jMenuBar) {
						found = true;
						break;
					}
				}
				this.add(jMenuBar, BorderLayout.NORTH);
				if (!found) {
					this.add(jMenuBar, BorderLayout.NORTH);
				}
			}
		}
	}

	public void setSourceMode(boolean source) {
		if (source) {
			if (!isSourceWindowActive()) {
				toggleSourceWindow();
			}
		} else {
			if (isSourceWindowActive()) {
				toggleSourceWindow();
			}

		}
	}

	public void setEdit(boolean edit) {
		exclusiveEdit = edit;
		jtpMain.setEditable(edit);
		jtpMain.setOpaque(edit);
		// jtpMain.setFocusable(edit);
	}

	public void setSource(String source) {
		if (isSourceWindowActive()) {
			jtpSource.setText(UnicodeDecode.decode(source));
		} else {
			jtpMain.setText(source);
		}
		this.validate();
	}

	public String getSource() {
		String docTextCase;
		if (!(isSourceWindowActive())) {
			docTextCase = jtpMain.getText();
		} else {
			docTextCase = jtpSource.getText();
		}
		return docTextCase;
	}

	// 20080919 hashimoto
	// リンクをジャンプしてきた際に、リンク先の id からリンク箇所をハイライト表示＆カーソル移動する
	public void emphasize(String id) {
		
		String source = this.getSource("body");
		source = source.replaceAll("<br>", "\n");
		source = source.replaceAll("&amp;", "&");
		source = source.replaceAll("&quot;", "\"");
		// ユーザが入力したデータに<>がある場合、リンクの検索に支障がでるので、_に置換する.
		source = source.replaceAll("&gt;", "_");
		source = source.replaceAll("&lt;", "_");

		int fromIndex;
		int sourceIndex = 0;
		int endTagIndex;
		int counter = 0;
		boolean flag = false;
		char ch;
		int wysiwygIndex;
		int wysiwygLength;

		// 開始タグのhref attribute のインデックス
		fromIndex = source.indexOf("href=\"" + id+"\"");
		// 開始タグ直後の文字のインデックス
		sourceIndex = source.indexOf(">", fromIndex) + 1;
		// 終了タグのインデックス
		endTagIndex = source.indexOf("<", sourceIndex);
		if (fromIndex != -1) {
			// 開始タグ以前のすべてのタグの文字数をカウントする
			for (int i = 0; i <= sourceIndex; i++) {
				ch = source.charAt(i);
				if (ch == '<') { // タグの先頭
					counter++;
					counter = special_begin_tags_adjustment(source,i,counter);
					flag = true;
				} else if (ch == '>') { // タグの終端
					counter++;
					counter = special_end_tags_adjustment(source,i,counter);
					flag = false;
				} else if (flag == true) { // タグ中の文字
					counter++;
				} else if (flag == false) { // タグ以外のテキスト
				}
			}
			wysiwygIndex = sourceIndex - counter + 1;
			wysiwygLength = endTagIndex - sourceIndex;
//			System.out.printf("%s\n%s\n%s\n%s\n",source.substring(0,9),source.substring(10,19),source.substring(20,29),source.substring(30,39));
//			System.out.printf("fromIndex:%d,sourceIndex:%d,endTagIndex:%d,counter:%d\n",fromIndex,sourceIndex,endTagIndex,counter);
			
			setCaretPosition(0);
			setCaretPosition(wysiwygIndex+wysiwygLength);
			
			Highlighter hilite = this.jtpMain.getHighlighter();
			if (!isKeepHighlights()) hilite.removeAllHighlights();
			DefaultHighlightPainter hilightPainter = new DefaultHighlightPainter(
					Color.yellow);
			try {
				hilite.addHighlight(wysiwygIndex, wysiwygIndex + wysiwygLength,
						hilightPainter);
			} catch (BadLocationException e2) {
				e2.printStackTrace();
			}
			jtpMain.addFocusListener(new GSHTMLEditorTextPaneFocusListener(jtpMain,wysiwygIndex,wysiwygLength));
			jtpMain.requestFocusInWindow();
		}
	}
	
	private int special_begin_tags_adjustment(String source, int index, int counter) {
		String[] tagNames = {"ol","ul","li"};
		for (int i = 0; i<tagNames.length;i++){
			String tagName = tagNames[i];
			if (source.startsWith(tagName,i)){
				return counter-1;
			} 
		}
		return counter;
	}
	
	private int special_end_tags_adjustment(String source, int index, int counter) {
		String[] tagNames = {"/li", "/ol","/ul","/h1","/h2","/h3","/h4"};
		for (int i = 0; i<tagNames.length;i++){
			String tagName = tagNames[i];
			if (endsWith(source,index,tagName)){
				return counter-1;
			} 
		}
		return counter;
	}

	private boolean endsWith(String source, int i, String tagName) {
		boolean ans;
		int tagNameLength = tagName.length();
		String stringToBeCompared = source.substring(i-tagNameLength,i);
		ans = stringToBeCompared.equals(tagName);
		return ans;
	}

	public void emphasize(int start, int length) {
		int wysiwygIndex = start+1;
		int wysiwygLength = length;
		
		String source = this.getSource("body");
		source = source.replaceAll("<br>", "\n");
		source = source.replaceAll("&amp;", "&");
		source = source.replaceAll("&quot;", "\"");
		// ユーザが入力したデータに<>がある場合、リンクの検索に支障がでるので、_に置換する.
		source = source.replaceAll("&gt;", "_");
		source = source.replaceAll("&lt;", "_");
		
		setCaretPosition(0);
		setCaretPosition(wysiwygIndex+wysiwygLength);
		
		Highlighter hilite = this.jtpMain.getHighlighter();
		if (!isKeepHighlights()) hilite.removeAllHighlights();
		DefaultHighlightPainter hilightPainter = new DefaultHighlightPainter(
				Color.yellow);
		try {
			hilite.addHighlight(wysiwygIndex, wysiwygIndex + wysiwygLength,
					hilightPainter);
		} catch (BadLocationException e2) {
			e2.printStackTrace();
		}
		jtpMain.addFocusListener(new GSHTMLEditorTextPaneFocusListener(jtpMain,wysiwygIndex,wysiwygLength));
		jtpMain.requestFocusInWindow();	
	}
	
//	private int number_of_special_tags_ocurrences (String source) {
////		String[] tagNames = {"h1", "h2", "h3", "h4", "li", "ol", "H1", "H2", "H3", "H4", "LI", "OL"};
//		String[] tagNames = {"li", "ol", "LI", "OL"};
//		int start_index = 0;
//		int accum = 0;
//		for (int i = 0; i<tagNames.length;i++){
//			String tagName = tagNames[i];
//			while (start_index >=0) {
//				int next_index = source.indexOf(tagName,start_index);
//				if (next_index >= 0) {
//					accum++;
//					start_index = next_index;
//				} else {
//					start_index = -1;
//				}
//			}
//		}
//		return accum;
//	}

	public void paste() {
		Clipboard clp = getToolkit().getSystemClipboard();
		try{
			Transferable data = clp.getContents(this);
			String strClip = (String)data.getTransferData(DataFlavor.stringFlavor);
			strClip = strClip.replaceAll(" ", "\240");
			String [] strClips = strClip.split("\n");
			boolean lastIsReturn = false;
			if( strClip.endsWith("\n") ){
				String [] tmp = strClips;
				strClips = new String[tmp.length+1];
				System.arraycopy(tmp,0,strClips,0,tmp.length);
				lastIsReturn = true;
			}
			for(int i = 0; i < strClips.length; i++){
				int caretPos = jtpMain.getCaretPosition();
				htmlDoc.insertString(caretPos, strClips[i], jtpMain.getInputAttributes());
				if( i != strClips.length-1 ){
					int caretPos2 = jtpMain.getCaretPosition();
					htmlKit.insertHTML(htmlDoc, caretPos2, "<BR>", 0, 0, HTML.Tag.BR);
				}
			}
			if(lastIsReturn&&jtpMain.getCaretPosition()==htmlDoc.getLength()-1){
				jtpMain.setCaretPosition(jtpMain.getCaretPosition()+1);
			}
			
		}catch(Exception e){}
	}
	
	public void setSearchAction(GSHTMLEditorSearchActionListner searchActionListner){
		this.searchActionListner = searchActionListner;
	}
	public void setFocusAction(GSHTMLEditorFoucsActionListner foucsActionListner){
		this.foucsActionListner = foucsActionListner;
	}
	
	public void requestFocusX(){
		if (!(isSourceWindowActive())) {
			jtpMain.requestFocus();
		} else {
			jtpSource.requestFocus();
		}
	}

	class GSHTMLEditorTextPaneFocusListener implements FocusListener{
		GSHTMLEditorTextPane textPane;
		int index;
		int length;
		private GSHTMLEditorTextPaneFocusListener(GSHTMLEditorTextPane textPane,int index, int length){
			this.textPane = textPane;
			this.index = index;
			this.length = length;
		}
		public void focusGained(FocusEvent e1){
			Highlighter hilite = this.textPane.getHighlighter();
			if (!isKeepHighlights()) hilite.removeAllHighlights();
			DefaultHighlightPainter hilightPainter = new DefaultHighlightPainter(
					Color.yellow);
			try {
				hilite.addHighlight(index, index + length,
						hilightPainter);
			} catch (BadLocationException e2) {
				e2.printStackTrace();
			}
			textPane.setCaretPosition(index);
		}
		public void focusLost(FocusEvent e){
			textPane.removeFocusListener(this);
			Highlighter hilite = this.textPane.getHighlighter();
			if (!isKeepHighlights()) hilite.removeAllHighlights();
		}
	}

}
