package asandatabasebrowser.view;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.lang.ref.WeakReference;
import java.math.BigDecimal;
import java.sql.Types;
import java.util.Comparator;

import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPopupMenu;
import javax.swing.border.LineBorder;

import asandatabasebrowser.Global;
import asandatabasebrowser.AsanDatabaseBrowser;
import asandatabasebrowser.action.CreateInsertAction;
import asandatabasebrowser.action.CreateUpdateAction;
import asandatabasebrowser.model.ColumnInfo;
import asandatabasebrowser.model.DbData;
import asandatabasebrowser.model.ITableInfo;
import asandatabasebrowser.model.TableInfo;
import asandatabasebrowser.model.VvDatabase;

import net.sourceforge.swingx.Logger;
import net.sourceforge.swingx.SxMouseAdapter;
import net.sourceforge.swingx.SxOptionPane;
import net.sourceforge.swingx.jspread.JSpread;
import net.sourceforge.swingx.jspread.JSpreadCellEditor;
import net.sourceforge.swingx.jspread.JSpreadCellRenderer;
import net.sourceforge.swingx.jspread.JSpreadCellEditor.DefaultCellEditor;

/** 
 * 編集可能なビュー。
 */
public class DbTableView extends JSpread {
	private static Logger logger = Logger.getLogger(DbTableView.class);
	static final int FONT_WIDTH = 8;
	static final int MAX_HEADER_WIDTH = 300;
	public DbData data;
//	DbTableModel model = new DbTableModel();
	private CellRenderer2 renderer = new CellRenderer2();
	static JSpreadCellEditor numericEditor = new JSpreadCellEditor.NumberEditor();
	static JSpreadCellEditor charEditor = new JSpreadCellEditor.DefaultCellEditor();
	static JSpreadCellEditor dateEditor = new DateEditor();
	static JSpreadCellEditor timeEditor = new TimeEditor();
	static JSpreadCellEditor timestampEditor = new TimestampEditor();
	static JSpreadCellEditor bitEditor = new BitEditor();
	
	
	DbTableView(DbData data) {
		//assert frame!=null;
		this.data = data;
		super.createScrollView();
		this.getSheet().allowAutoRowHeaderResize = false;
		updateData(data);
		fitAll();
//		this.setModel(model);
//		//System.out.println("autoResizeMode="+getAutoResizeMode());
//		setAutoResizeMode(AUTO_RESIZE_OFF);
//		getTableHeader().addMouseListener(new DbTableHeaderMouseListener());
//		this.getTableHeader().setReorderingAllowed(false); // ヘッダのドラッグは無効 
//		this.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
//		this.setColumnSelectionAllowed(true);
//		this.setRowSelectionAllowed(true);
		//setToolTipText("てーぶる");
		setToolTipEnabled(CELL_PANE_NAME, true);
		setToolTipEnabled(COLUMN_HEADER_PANE_NAME, true);
		getComponent(CELL_PANE_NAME).addMouseListener(new CellMouseListener());
		getComponent(COLUMN_HEADER_PANE_NAME).addMouseListener(new HeaderMouseListener());
		getComponent(ROW_HEADER_PANE_NAME).addMouseListener(new RowMouseListener());

	}

	
//	void updateTableModel() {
//		model.fireTableDataChanged();
////		((DefaultTableColumnModel)getColumnModel()).fireColumnMarginChanged();
//	}
	public boolean isCellEditable(int row, int column) {
		//System.out.println(""+data.getMode(row)+" "+(data.getMode(row) != DbData.NONE));
		int mode = data.getMode(row);
		return mode == DbData.INSERT || mode == DbData.UPDATE;
	}
	public JSpreadCellEditor getCellEditor(Object value, 
			int rowDataIndex, int colDataIndex) {
		TableInfo table = (TableInfo) data.tableInfo;
		short dataType = table.getColumnInfo(colDataIndex).dataType;
		switch (dataType) {
		case Types.BIT:				// -7
			return bitEditor;
		case Types.TINYINT:         // -6
		case Types.BIGINT:          // -5
		case Types.LONGVARBINARY:   // -4
		case Types.VARBINARY:       // -3
		case Types.BINARY:          // -2
		case Types.LONGVARCHAR:     // -1
		case Types.NULL:            // 0
			System.out.println("dataType="+dataType+" typeName="+table.getColumnInfo(colDataIndex).typeName);
			AsanDatabaseBrowser.theApp.showMessage(this, 
					"この型のエディタはサポートされていません。\n"+
					"dataType="+dataType+" typeName="+table.getColumnInfo(colDataIndex).typeName, 
					null, SxOptionPane.WARNING_MESSAGE);
			return null;
		case Types.CHAR:            // 1
		case Types.VARCHAR:         // 12
			return charEditor;
		case Types.NUMERIC:         // 2
		case Types.DECIMAL:         // 3
		case Types.INTEGER:         // 4
		case Types.SMALLINT:        // 5
		case Types.FLOAT:           // 6
		case Types.REAL:            // 7
		case Types.DOUBLE:          // 8
			return numericEditor;
		case Types.BOOLEAN:         // 16
		case Types.DATALINK:        // 70
			System.out.println("dataType="+dataType+" typeName="+table.getColumnInfo(colDataIndex).typeName);
			AsanDatabaseBrowser.theApp.showMessage(this, 
					"この型のエディタはサポートされていません。\n"+
					"dataType="+dataType+" typeName="+table.getColumnInfo(colDataIndex).typeName, 
					null, SxOptionPane.WARNING_MESSAGE);
			return null;
		case Types.DATE:            // 91
			return dateEditor;
		case Types.TIME:            // 92
			return timeEditor;
		case Types.TIMESTAMP:       // 93
			return timestampEditor;
		case Types.OTHER:           // 1111
		case Types.JAVA_OBJECT:     // 2000
		case Types.DISTINCT:        // 2001
		case Types.STRUCT:          // 2002
		case Types.ARRAY:           // 2003
		case Types.BLOB:            // 2004
		case Types.CLOB:            // 2005
		case Types.REF:             // 2006
			System.out.println("dataType="+dataType+" typeName="+table.getColumnInfo(colDataIndex).typeName);
			AsanDatabaseBrowser.theApp.showMessage(this, 
					"この型のエディタはサポートされていません。\n"+
					"dataType="+dataType+" typeName="+table.getColumnInfo(colDataIndex).typeName, 
					null, SxOptionPane.WARNING_MESSAGE);
			return null;
		default:
			assert false: dataType;
			return null;
		}
		//JSpreadCellEditor editor = super.getCellEditor(value, rowDataIndex, colDataIndex);
		//System.out.println("value="+value+" editor="+editor);
		//return editor;
	}
	public Object getValueAt(int rowDataIndex, int colDataIndex) {
		return data.getRecord(rowDataIndex)[colDataIndex];
	}
	public void setValueAt(Object value, int rowDataIndex, int colDataIndex) {
		//logger.debug("setValue() value="+value+" rowDataIndex="+rowDataIndex+" colDataIndex="+colDataIndex);
		assert 0 <= rowDataIndex: rowDataIndex;
		assert 0 <= colDataIndex: colDataIndex;
		int mode = data.getMode(rowDataIndex);
		assert mode == DbData.INSERT || mode == DbData.UPDATE;
		super.setValueAt(value, rowDataIndex, colDataIndex);
		data.getRecord(rowDataIndex)[colDataIndex] = value;
		repaint();
	}

	public Object getHeaderValueAt(int header, int dataIndex) {
		switch (header) {
		case ROW:
			switch (data.getMode(dataIndex)) {
			case DbData.INSERT:
				return Global.recordInsertIcon;
			case DbData.UPDATE:
				return Global.recordUpdateIcon;
			case DbData.DELETE:
				return Global.recordDeleteIcon;
			case DbData.NONE:
				return ""+(dataIndex+1);
			default:
				assert false: data.getMode(dataIndex);
				break;
			}
			return null;	// ここには到達しない
		case COLUMN:
			return data.tableInfo.getColumnName(dataIndex);
		case CORNER:
			return null;
		}
		return null;	// ここには到達しない
	}

	/**
	 * セルを描画するのに使用されるコンポーネントを返します.
	 * @param value				セルの値(null可)
	 * @param rowDataIndex		列表示番号(dataIndex)(0..)
	 * @param columnDataIndex	行表示番号(dataIndex)(0..)
	 * @return セルを描画するのに使用されるコンポーネント(null可)
	 */ 
	public JSpreadCellRenderer getCellRenderer(Object value, 
			int rowDataIndex, int columnDataIndex) {
		assert 0 <= rowDataIndex: rowDataIndex;
		assert 0 <= columnDataIndex: columnDataIndex;
		return renderer;
	}
	
	void updateData(DbData data) {
		System.out.println("updateData() "+data.tableInfo);
		ITableInfo info0 = this.data.tableInfo;
		this.data = data;
		this.setCellCount(data.getRecordCount(),
				data.tableInfo.getColumnCount(), null);
		if (! info0.equals(data.tableInfo)) {
			fitAll();
		}
		this.再配置();
	}
	private void fitAll() {
		// 最適なサイズを指定
		for (int c=0; c<data.tableInfo.getColumnCount(); c++) {
			int w = getFitWidth(c, true);
			getSheet().getHeader(JSpread.COLUMN).setPreferredWidth(c, w);
			getSheet().getHeader(JSpread.COLUMN).setWidth(c, w);
		}		
	}
	private int getFitWidth(int col, boolean containHeader) {
		int prefWidth = 0;
		if (containHeader) {		// ヘッダの幅も合わせるか？
			int len = data.tableInfo.getColumnName(col).length();
			prefWidth = len * FONT_WIDTH + 4;
		}
		for (int r=0; r<data.getRecordCount(); r++) {
			Object[] record = data.getRecord(r);
			int len = (record[col] != null) ? record[col].toString().length() : 4;
			int size = len * FONT_WIDTH + 4;
			if (size > prefWidth) {
				prefWidth = size;
				if (prefWidth >= MAX_HEADER_WIDTH) break;
			}
		}
		return Math.min(prefWidth, MAX_HEADER_WIDTH);		// +2は左右のPadding
	}	
	/** マウス位置のセルごとにツールチップの文字列を返す。*/
	protected String getToolTipText(String panename, MouseEvent e) {
		if (panename.equals(CELL_PANE_NAME)) {
			int col = getViewPositionAtPoint(COLUMN, e.getX());
			if (col == -1) return null;
			int row = getViewPositionAtPoint(ROW, e.getY());
			if (row == -1) return null;
			//System.out.println("col="+col+" row="+row);
			return ""+getValueAt(row, col);
		} else if (panename.equals(COLUMN_HEADER_PANE_NAME)) {
			int col = getViewPositionAtPoint(COLUMN, e.getX());
			if (col == -1) return null;
			TableInfo table = (TableInfo) data.tableInfo;
			return table.getDatabase().getToolTipText(table, table.getColumnInfo(col));
		}
		assert false: panename;
		return null;
	}
	/** 
	 * 選択されているレコードのindexを返す.
	 * 選択されていないときには、サイズ０の配列を返す。 
	 * @deprecated	このメソッドは件数が１万件だととても遅い！高速化すべき。 
	 */
//	public int[] getSelectedRecordIndex() {
//		System.out.println("getSelectedRecordIndex() enter "+System.currentTimeMillis());
//		ArrayList result = new ArrayList();
//		//JSpreadHeader header = this.getHeader(JSpread.COLUMN);
//		for (int i=0; i<this.data.getRecordCount(); i++) {
//			// １つのセルでも選択さていたら、そのレコードは選択されているとみなす。
//			if (isSelectedRecord(i)) {
//				result.add(new Integer(i));
//			}
//		}
//		int[] result2 = new int[result.size()];
//		for (int i=0; i<result.size(); i++) {
//			result2[i] = ((Integer) result.get(i)).intValue();
//		}
//		System.out.println("getSelectedRecordIndex() exit  "+System.currentTimeMillis());
//		return result2;
//	}
	public boolean[] getSelection() {
		System.out.println("getSelection() enter "+System.currentTimeMillis());
		boolean[] result = new boolean[data.getRecordCount()];
		for (int i=0; i<this.data.getRecordCount(); i++) {
			result[i] = isSelectedRecord(i);
		}
		System.out.println("getSelection() exit  "+System.currentTimeMillis());
		return result;
	}
	/** 指定されたレコードが選択されているかどうかを返します。 */
	public boolean isSelectedRecord(int row) {
		// １つのセルでも選択さていたら、そのレコードは選択されているとみなす。
		int count = this.getHeader(JSpread.COLUMN).getViewCount();
		for (int c=0; c<count; c++) {
			if (this.getCellSelection(row, c)) {
				return true;
			}
		}
		return false;
	}

	boolean isSelectEditingCell() {
		for (int r=0; r<data.getRecordCount(); r++) {
			int mode = data.getMode(r);
			if (mode == DbData.INSERT || mode == DbData.UPDATE) {
				Object[] rec = data.getRecord(r);
				for (int c=0; c<rec.length; c++) {
					if (getCellSelection(r, c)) {
						return true;
					}
				}
			}
		}
		return false;
	}
	/** セル・レンダラー	*/
	class CellRenderer2 extends JSpreadCellRenderer.DefaultCellRenderer {
		public JComponent getSpreadCellRendererComponent(JSpread spread, Object value, 
				boolean isSelected, boolean hasFocus, 
				int rowDataIndex, int colDataIndex) {
			JLabel label = (JLabel) super.getSpreadCellRendererComponent(spread, value, isSelected, hasFocus, rowDataIndex, colDataIndex);
			ColumnInfo col = (ColumnInfo) data.tableInfo.getIColumnInfo(colDataIndex);
			if (! isSelected) {
				switch (data.getMode(rowDataIndex)) {
				case DbData.NONE:
					label.setBackground(Color.WHITE);
					break;
				case DbData.INSERT:
					label.setBackground(Color.YELLOW);
					break;
				case DbData.UPDATE:
					label.setBackground(Color.GREEN);
					break;
				case DbData.DELETE:
					label.setBackground(Color.RED);
					break;
				default:
					assert false:data.getMode(rowDataIndex);
				}
			}
			// NULLか？
			if (value == null) {
				label.setText("NULL");
				label.setForeground(Color.GRAY);
				label.setHorizontalAlignment(JLabel.CENTER);
				return label;
			}
			// 数値の時には右寄せで表示
			if (value instanceof Integer ||
				value instanceof Long ||
				value instanceof BigDecimal) {
				label.setForeground(Color.BLACK);
				label.setHorizontalAlignment(JLabel.RIGHT);
				return label;
			}
			// BLOB,CLOB,RAWなど巨大
			if (value instanceof WeakReference) {
				label.setText(col.typeName);
				label.setForeground(Color.BLACK);
				label.setHorizontalAlignment(JLabel.CENTER);
				return label;
			}
			// その他
			label.setForeground(Color.BLACK);
			label.setHorizontalAlignment(JLabel.LEFT);
			return label;
		}
	}
	/**
	 * 日時を編集するセル・エディタです.
	 * javax.swing.JTextField で編集を行います。
	 * 日時を表示・解釈する日時フォーマットを指定することができます。
	 * @author Asan
	 */
	static class DateEditor extends DefaultCellEditor {
		public void setCellValue(Object value) {
			textField.setText((value == null) ? "" : value.toString());
		}
		public Object getCellValue() { 
			String s = textField.getText();
			if (s.length()==0) return null;
		    try { 
		    	return java.sql.Date.valueOf(s);
		    } catch (IllegalArgumentException e) {
		    	logger.debug(s, e);
				textField.setBorder(new LineBorder(Color.red));
				AsanDatabaseBrowser.theApp.showMessage(textField, 
						"データ変換エラー["+s+"]", 
						e, SxOptionPane.ERROR_MESSAGE);
				return value; 
		    }
		}
	}	
	static class TimeEditor extends DefaultCellEditor {
		public void setCellValue(Object value) {
			textField.setText((value == null) ? "" : value.toString());
		}
		public Object getCellValue() { 
			String s = textField.getText();
			if (s.length()==0) return null;
		    try { 
		    	return java.sql.Time.valueOf(s);
		    } catch (IllegalArgumentException e) {
		    	logger.debug(s, e);
				textField.setBorder(new LineBorder(Color.red));
				AsanDatabaseBrowser.theApp.showMessage(textField, 
						"データ変換エラー["+s+"]", 
						e, SxOptionPane.ERROR_MESSAGE);
				return value; 
		    }
		}
	}	
	static class TimestampEditor extends DefaultCellEditor {
		public void setCellValue(Object value) {
			textField.setText((value == null) ? "" : value.toString());
		}
		public Object getCellValue() { 
			String s = textField.getText();
			if (s.length()==0) return null;
		    try { 
		    	return java.sql.Timestamp.valueOf(s);
		    } catch (IllegalArgumentException e) {
		    	logger.debug(s, e);
				textField.setBorder(new LineBorder(Color.red));
				AsanDatabaseBrowser.theApp.showMessage(textField, 
						"データ変換エラー["+s+"]", 
						e, SxOptionPane.ERROR_MESSAGE);
				return value; 
		    }
		}
	}	
	static class BitEditor extends DefaultCellEditor {
		public void setCellValue(Object value) {
			textField.setText((value == null) ? "null" : value.toString());
		}
		public Object getCellValue() { 
			String s = textField.getText();
			if (s.length()==0) return null;
		    try { 
		    	return Boolean.valueOf(s);
		    } catch (IllegalArgumentException e) {
		    	logger.debug(s, e);
				textField.setBorder(new LineBorder(Color.red));
				AsanDatabaseBrowser.theApp.showMessage(textField, 
						"データ変換エラー["+s+"]", 
						e, SxOptionPane.ERROR_MESSAGE);
				return value; 
		    }
		}
	}	
	/**
	 * セルをダブルクリックしたら、カラム情報を開く。
	 */
	class CellMouseListener extends SxMouseAdapter {
		/** カラム名をダブルクリックしたら、カラム情報を開く。 */
		public void mouseClicked(MouseEvent e) {
			//System.out.println(e);
			try {
				// 左ボタンがダブルクリックされたら、LargeObjectの場合、その内容を表示する。
				if (e.getClickCount()==2 && (e.getModifiers()&MouseEvent.BUTTON1_MASK)!=0) {
					int col = getViewPositionAtPoint(COLUMN, e.getX());
					if (col == -1) return;
					int row = getViewPositionAtPoint(ROW, e.getY());
					if (row == -1) return;
					//System.out.println("col="+col+" row="+row);
					Object value = getValueAt(row, col);
					//　BLOB/CLOBなどのとき、内容を表示する。 
					AsanDatabaseBrowser.theApp.openLargeObject(value, data.tableInfo, data.getRecord(row), col);
				}
			}
			catch (Exception ex) {
				AsanDatabaseBrowser.theApp.showMessage(DbTableView.this, 
						"エラーが発生しました", ex, SxOptionPane.ERROR_MESSAGE);
			}
		}
		/**  ポップアップメニューを表示する。 */
		public void mousePopup(MouseEvent e) {
			//System.out.println("Popup!");
//			int row = getViewPositionAtPoint(COLUMN, e.getX());
			JPopupMenu popup = new JPopupMenu();
//			popup.add(new CopyClipboardAction(DbTableView.this));
			popup.add(getActionMap().get("CopyClipboardAction"));
			popup.add(getActionMap().get("SelectAllAction"));
			popup.addSeparator();
			if (isSelectEditingCell()) {
				popup.add(new SetNullAction());
				popup.add(new SetDefaultAction());
				popup.add(new SetOriginalAction());
				popup.addSeparator();
			}
			VvDatabase db = data.tableInfo.getDatabase();
			popup.add(new CreateInsertAction(AsanDatabaseBrowser.theApp, db, DbTableView.this));
			popup.add(new CreateUpdateAction(AsanDatabaseBrowser.theApp, db, DbTableView.this));
			popup.show(e.getComponent(), e.getX(), e.getY());
		}
	}
	/**
	 * 列ヘッダをダブルクリックしたら
	 */
	class RowMouseListener extends SxMouseAdapter {
		/**  ポップアップメニューを表示する。 */
		public void mousePopup(MouseEvent e) {
			//System.out.println("Popup!");
			JPopupMenu popup = new JPopupMenu();
			popup.add(getActionMap().get("CopyClipboardAction"));
			popup.add(getActionMap().get("SelectAllAction"));
			popup.addSeparator();
			VvDatabase db = data.tableInfo.getDatabase();
			popup.add(new CreateInsertAction(AsanDatabaseBrowser.theApp, db, DbTableView.this));
			popup.add(new CreateUpdateAction(AsanDatabaseBrowser.theApp, db, DbTableView.this));
			popup.show(e.getComponent(), e.getX(), e.getY());
		}
	}	
	/** ヘッダでマウスクリックされたとき、ポップアップメニューを表示して、ソートを行う。 */
	class HeaderMouseListener extends SxMouseAdapter {
		public void mouseClicked(MouseEvent e) {
			//System.out.println(e);
			// 左ボタンがダブルクリックされたら、境界の場合、幅をフィットさせる。
			if (e.getClickCount()==2 && (e.getModifiers()&MouseEvent.BUTTON1_MASK)!=0) {
				// 境界か？
				int col0 = getViewPositionAtPoint(COLUMN, e.getX()-3);
				int col1 = getViewPositionAtPoint(COLUMN, e.getX()+3);
				if (col0 != col1) {
					// もし、左のヘッダが選択されていれば、
					if (getHeaderSelection(COLUMN, col0)) {
						// 選択されているヘッダすべての幅をフィットさせる。
						for (int c=0; c<getCellCount(COLUMN); c++) {
							if (getHeaderSelection(COLUMN, c)) {
								int w = getFitWidth(c, true);
								getSheet().getHeader(JSpread.COLUMN).setPreferredWidth(c, w);
								getSheet().getHeader(JSpread.COLUMN).setWidth(c, w);									
							}
						}
					} else {
						// 左のヘッダの幅のみをフィットさせる。
						int w = getFitWidth(col0, true);
						getSheet().getHeader(JSpread.COLUMN).setPreferredWidth(col0, w);
						getSheet().getHeader(JSpread.COLUMN).setWidth(col0, w);									
					}
					再配置();
					e.consume();
				}
			}

		}
		/**  ポップアップメニューを表示する。 */
		public void mousePopup(MouseEvent e) {
			int col = getViewPositionAtPoint(COLUMN, e.getX());
			if (col == -1) return;
			System.out.println("Popup! "+e);
			JPopupMenu popup = new JPopupMenu();
			popup.add(new SortAction(col, true, "昇順にソート"));
			popup.add(new SortAction(col, false, "降順にソート"));
			popup.show(e.getComponent(), e.getX(), e.getY());
		}
	}
	
	/** ソートを行う */
	class SortAction extends AbstractAction implements Comparator {
		private int col;
		private boolean asc;
		SortAction(int col, boolean asc, String title) {
			super(title);
			this.col = col;
			this.asc = asc;
			String icon = asc ? "/resources/SortAsc.png" : "/resources/SortDsc.png";
			putValue(SMALL_ICON, Global.getImageIcon(icon));
			//putValue(MNEMONIC_KEY, new Integer('A'));
			putValue(SHORT_DESCRIPTION, title);
			putValue(LONG_DESCRIPTION, title);
			
		}
		public void actionPerformed(ActionEvent e) {
			System.out.println(e);
			data.sort(this);
			updateData(data);
			再描画();
			//getSelectionModel().clearSelection();
//			e.consume();
		}

		/** Comparatorインターフェースのメソッド */
		public int compare(Object o1, Object o2) {
			return asc ? compare0(o1, o2) : -compare0(o1, o2);
		}
		public int compare0(Object o1, Object o2) {
			Object[] rec1 = (Object[]) o1;
			Object[] rec2 = (Object[]) o2;
			Object fld1 = rec1[col];
			Object fld2 = rec2[col];
			if (fld1==fld2) return 0;
			if (fld1==null) return -1;	// TODO:NULLを先頭にもってくるか？末尾に持っていくか？
			if (fld2==null) return 1;
			if (fld1 instanceof Comparable) {
				Comparable c1 = (Comparable) fld1;
				Comparable c2 = (Comparable) fld2;
				return c1.compareTo(c2);
			}
			System.out.println("比較できない型です "+fld1.getClass());
			return 0;
		}
		// Comparatorインターフェースのメソッド
		public boolean equals(Object obj) {
			return this==obj;
		}
	}
	/** 編集中のレコードのうち、選択されたセルをNULLにする。 */
	class SetNullAction extends AbstractAction {
		SetNullAction() {
			super("選択されたセルを NULL にする(N)");
			putValue(MNEMONIC_KEY, new Integer('N'));
			putValue(SHORT_DESCRIPTION, "選択されたセルを NULL にする");
			putValue(LONG_DESCRIPTION, "選択されたセルを NULL にする");
		}
		public void actionPerformed(ActionEvent ev) {
			for (int r=0; r<data.getRecordCount(); r++) {
				int mode = data.getMode(r);
				if (mode == DbData.INSERT || mode == DbData.UPDATE) {
					Object[] rec = data.getRecord(r);
					for (int c=0; c<rec.length; c++) {
						if (getCellSelection(r, c)) {
							System.out.println(rec[c]);
							rec[c] = null;
						}
					}
				}
			}
			repaint();
		}
	}
	/** 編集中のレコードのうち、選択されたセルをデフォルト値にする。 */
	class SetDefaultAction extends AbstractAction {
		SetDefaultAction() {
			super("選択されたセルをデフォルト値にする(D)");
			putValue(MNEMONIC_KEY, new Integer('D'));
			putValue(SHORT_DESCRIPTION, "選択されたセルをデフォルト値にする");
			putValue(LONG_DESCRIPTION, "選択されたセルをデフォルト値にする");
		}
		public void actionPerformed(ActionEvent ev) {
			TableInfo table = (TableInfo) data.tableInfo;
			for (int r=0; r<data.getRecordCount(); r++) {
				int mode = data.getMode(r);
				if (mode == DbData.INSERT || mode == DbData.UPDATE) {
					Object[] rec = data.getRecord(r);
					for (int c=0; c<rec.length; c++) {
						if (getCellSelection(r, c)) {
							rec[c] = table.getColumnInfo(c).columnDefault;
						}
					}
				}
			}
			repaint();
		}
	}
	/** 編集中のレコードのうち、選択されたセルを編集前の値にする。 */
	class SetOriginalAction extends AbstractAction {
		SetOriginalAction() {
			super("選択されたセルを編集前の値にする(O)");
			putValue(MNEMONIC_KEY, new Integer('O'));
			putValue(SHORT_DESCRIPTION, "選択されたセルを編集前の値にする");
			putValue(LONG_DESCRIPTION, "選択されたセルを編集前の値にする");
		}
		public void actionPerformed(ActionEvent ev) {
			for (int r=0; r<data.getRecordCount(); r++) {
				int mode = data.getMode(r);
				if (mode == DbData.INSERT || mode == DbData.UPDATE) {
					Object[] rec = data.getRecord(r);
					for (int c=0; c<rec.length; c++) {
						if (getCellSelection(r, c)) {
							rec[c] = data.getOriginalValue(r, c);
						}
					}
				}
			}
			repaint();
		}
	}
}


