/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: AttributesTable.java
 *
 * Copyright (c) 2004, Static Free Software. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.tool.user.dialogs;

import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.change.DatabaseChangeEvent;
import com.sun.electric.database.change.DatabaseChangeListener;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Cell.CellGroup;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.CodeExpression;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.UserInterfaceMain;

import java.awt.Font;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import javax.swing.DefaultCellEditor;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;

/**
 * Class to define the Attributes panel in other dialogs.
 */
public class AttributesTable extends JTable implements DatabaseChangeListener
{

	/**
	 * Class to define attributes on nodes or arcs.
	 */
	public static class AttValPair
	{
		Variable.Key key;
		String trueName;
		String value;
		String eval;
		boolean code;
	}

	/**
	 * Model for storing Table data
	 */
	private static class VariableTableModel extends AbstractTableModel
	{
		private List<VarEntry> vars;                     // list of variables to display
		private static final String [] columnNames = { "Name", "Value", "Code", "Display", "Units" };
		private boolean showCode = true;
		private boolean showDispPos = false;
		private boolean showUnits = false;
		private List<VarEntry> varsToDelete;

		private static class VarEntry {
			// current state of var entry
			private String varTrueName;
			private Variable.Key varKey;
			private Object value;
			private CodeExpression.Code code;
			private TextDescriptor.DispPos dispPos;
			private TextDescriptor.Unit units;
			private boolean display;
			private ElectricObject owner;
			// if var is null, this means create a new var
			// if any of the above values are different from the original (below) var's value, modify
			// if this is in the varsToDelete list, delete it
			private Variable var;
			// initial state of var entry
			private String initialVarTrueName;
			private Object initialValue;
			private CodeExpression.Code initialCode;
			private TextDescriptor.DispPos initialDispPos;
			private TextDescriptor.Unit initialUnits;
			private boolean initialDisplay;

			private VarEntry(ElectricObject owner, Variable var) {
				this.owner = owner;
				this.var = var;
				if (var == null) return;

				varKey = var.getKey();
				varTrueName = initialVarTrueName = var.getTrueName();
				value = initialValue = var.getObject();
				code = initialCode = var.getCode();
				dispPos = initialDispPos = var.getDispPart();
				units = initialUnits = var.getUnit();
				display = initialDisplay = var.isDisplay();
			}

			private String getName() { return varTrueName; }
			private Object getObject() { return value; }
			private CodeExpression.Code getCode() { return code; }
			private TextDescriptor.DispPos getDispPos() { return dispPos; }
			private TextDescriptor.Unit getUnits() { return units; }
			private boolean isDisplay() { return display; }
			private ElectricObject getOwner() { return owner; }
			private Variable.Key getKey() { return varKey; }

			private boolean isChanged() {
				if (!varTrueName.equals(initialVarTrueName)) return true;
				if (value != initialValue) return true;
				if (code != initialCode) return true;
				if (display != initialDisplay) return true;
				if (dispPos != initialDispPos) return true;
				if (units != initialUnits) return true;
				return false;
			}
		}

		/**
		 * Class to sort Variables by name.
		 */
		public static class VarEntrySort implements Comparator<VarEntry>
		{
			public int compare(VarEntry v1, VarEntry v2)
			{
				String s1 = v1.getName();
				String s2 = v2.getName();
				return s1.compareToIgnoreCase(s2);
			}
		}

		// constructor
		private VariableTableModel(boolean showCode, boolean showDispPos, boolean showUnits) {
			vars = new ArrayList<VarEntry>();
			varsToDelete = new ArrayList<VarEntry>();
			this.showCode = showCode;
			this.showDispPos = showDispPos;
			this.showUnits = showUnits;
		}

		/** Get number of columns in table */
		public int getColumnCount() {
			int c = 2;
			if (showCode) c++;
			if (showDispPos) c++;
			if (showUnits) c++;
			return c;
		}

		/** Get number of rows in table */
		public int getRowCount() { return vars.size(); }

		private int getCodeColumn() {
			if (!showCode) return -1;
			return 2;
		}

		private int getDispColumn() {
			if (!showDispPos) return -1;
			int c = 2;
			if (showCode) c++;
			return c;
		}

		private int getUnitsColumn() {
			if (!showUnits) return -1;
			int c = 2;
			if (showCode) c++;
			if (showDispPos) c++;
			return c;
		}

		/** Get object at location in table */
		public Object getValueAt(int rowIndex, int columnIndex)
		{
			// order: name, value
			VarEntry ve = vars.get(rowIndex);

			if (ve == null) return null;

			if (columnIndex == 0) {
				// name
				return ve.getName();
			}
			if (columnIndex == 1) {
				// value
				return ve.getObject().toString();
			}
			if (columnIndex == getCodeColumn()) {
				return ve.getCode();
			}
			if (columnIndex == getDispColumn()) {
				if (!ve.isDisplay()) return displaynone;
				return ve.getDispPos();
			}
			if (columnIndex == getUnitsColumn()) {
				return ve.getUnits();
			}
			return null;
		}

		/** Get column header names */
		public String getColumnName(int col) {
			if (col < 2)
				return columnNames[col];
			if (col == getCodeColumn()) return columnNames[2];
			if (col == getDispColumn()) return columnNames[3];
			if (col == getUnitsColumn()) return columnNames[4];
			return null;
		}

		/** See if cell is editable. */
		public boolean isCellEditable(int row, int col) {
			if (col > 0) return true;
			return false;
		}

		/** Set a value */
		public void setValueAt(Object aValue, int row, int col) {
			VarEntry ve = vars.get(row);
			ElectricObject owner = ve.getOwner();
			if (ve == null) return;
			if (owner == null) return;

			if (col == 0) return;                   // can't change var name

			if (col == 1) {
				if (!aValue.toString().equals(ve.getObject().toString())) {
					ve.value = aValue;
					fireTableCellUpdated(row, col);
				}
				return;
			}

			if (col == getCodeColumn()) {
				CodeExpression.Code newCode = (CodeExpression.Code)aValue;
				if (newCode != ve.getCode()) {
					ve.code = newCode;
					fireTableCellUpdated(row, col);
				}
				return;
			}

			if (col == getDispColumn()) {
				if (aValue == displaynone) {
					if (ve.isDisplay()) {
						ve.display = false;
						fireTableCellUpdated(row, col);
					}
				} else {
					TextDescriptor.DispPos newDispPos = (TextDescriptor.DispPos)aValue;
					if ((newDispPos != ve.getDispPos()) || !ve.isDisplay()) {
						ve.dispPos = newDispPos;
						ve.display = true;
						fireTableCellUpdated(row, col);
					}
				}
				return;
			}

			if (col == getUnitsColumn()) {
				TextDescriptor.Unit newUnit = (TextDescriptor.Unit)aValue;
				if (newUnit != ve.getUnits()) {
					ve.units = newUnit;
					fireTableCellUpdated(row, col);
				}
				return;
			}
		}

		/**
		 * Set this to be the list of variables shown
		 * @param owner ElectricObject which owns Variables
		 * @param variables the list of Variables to show
		 */
		private void setVars(ElectricObject owner, List<Variable> variables) {
			vars.clear();
			varsToDelete.clear();
			// sort by name
			for (Variable var : variables) {
				vars.add(new VarEntry(owner, var));
			}
			Collections.sort(vars, new VarEntrySort());
			fireTableDataChanged();
		}

		/** Add a variable to be displayed in the Table */
//		private void addVariable(Variable var) {
//			vars.add(new VarEntry(var));
//			Collections.sort(vars, new VarEntrySort());
//			fireTableDataChanged();
//		}

		private void clearVariables() {
			vars.clear();
			varsToDelete.clear();
			fireTableDataChanged();
		}

		public Class<?> getColumnClass(int col) {
			if (col == getCodeColumn()) return CodeExpression.Code.class;
			if (col == getDispColumn()) return Object.class;
			if (col == getUnitsColumn()) return TextDescriptor.Unit.class;
			return String.class;
		}

		public void setShowCode(boolean showCode) {
			if (this.showCode == showCode) return;
			this.showCode = showCode;
			fireTableStructureChanged();
		}

		public void setShowDisp(boolean showDisp) {
			if (this.showDispPos == showDisp) return;
			this.showDispPos = showDisp;
			fireTableStructureChanged();
		}

		public void setShowUnits(boolean showUnits) {
			if (this.showUnits == showUnits) return;
			this.showUnits = showUnits;
			fireTableStructureChanged();
		}

		/**
		 * Create a new var with default properties
		 */
		public void newVar(ElectricObject owner) {
			VarEntry ve = new VarEntry(owner, null);
			ve.var = null;
			ve.varKey = null;
			ve.varTrueName = getUniqueName("newVar");
			ve.value = "?";
			ve.code = CodeExpression.Code.NONE;
			ve.dispPos = TextDescriptor.DispPos.NAMEVALUE;
			ve.units = TextDescriptor.Unit.NONE;
			ve.display = true;

			vars.add(ve);
			Collections.sort(vars, new VarEntrySort());
			fireTableDataChanged();
		}

		/**
		 * Duplicate the variable in the specified row
		 * @param row the row containing the var to duplicate
		 */
		public void duplicateVar(int row) {
			if (row >= vars.size()) {
				JOptionPane.showMessageDialog(null, "Please select an attribute to duplicate",
					"Invalid Action", JOptionPane.WARNING_MESSAGE);
				return;
			}
			VarEntry srcVe = vars.get(row);

			VarEntry ve = new VarEntry(srcVe.getOwner(), null);
			ve.var = null;
			ve.varKey = null;
			ve.varTrueName = getUniqueName(srcVe.getName());
			ve.value = srcVe.getObject();
			ve.code = srcVe.getCode();
			ve.dispPos = srcVe.getDispPos();
			ve.units = srcVe.getUnits();
			ve.display = srcVe.isDisplay();

			vars.add(ve);
			Collections.sort(vars, new VarEntrySort());
			fireTableDataChanged();
		}

		/**
		 * Delete the var in the specified row
		 * @param row the row containing the var to delete
		 */
		public void deleteVar(int row) {
			if (row >= vars.size()) {
				JOptionPane.showMessageDialog(null, "Please select an attribute to delete",
					"Invalid Action", JOptionPane.WARNING_MESSAGE);
				return;
			}
			VarEntry ve = vars.remove(row);
			varsToDelete.add(ve);
			fireTableDataChanged();
		}

		/**
		 * Apply all new/delete/duplicate/modify changes
		 */
		public void applyChanges()
		{
			// prepare information about deleted attributes
			List<Variable.Key> deleteTheseVars = new ArrayList<Variable.Key>();
			ElectricObject owner = null;
			for (VarEntry ve : varsToDelete)
			{
				Variable var = ve.var;
				if (var == null) continue;
				owner = ve.getOwner();
				deleteTheseVars.add(var.getKey());
			}

			// prepare information about new and modified attributes
			List<Variable.Key> createKey = new ArrayList<Variable.Key>();
			List<Object> createValue = new ArrayList<Object>();
			List<Boolean> createNew = new ArrayList<Boolean>();
			List<Boolean> createDisplay = new ArrayList<Boolean>();
			List<CodeExpression.Code> createCode = new ArrayList<CodeExpression.Code>();
			List<Integer> createDispPos = new ArrayList<Integer>();
			List<Integer> createUnits = new ArrayList<Integer>();

			for(VarEntry ve : vars)
			{
				Variable var = ve.var;
				owner = ve.getOwner();
				Variable.Key newKey = null;
				Object newValue = ve.getObject();
				boolean newCreate = false;
				boolean newDisplay = ve.isDisplay();
				CodeExpression.Code newCode = ve.getCode();
				int newDispPos = ve.getDispPos().getIndex();
				int newUnits = ve.getUnits().getIndex();

				if (var == null)
				{
					// this is a new var
					newCreate = true;
					String name = ve.getName();
					if (!name.startsWith("ATTR_") && !name.startsWith("ATTRP_"))
						name = "ATTR_"+name;
					newKey = Variable.newKey(name);
				} else
				{
					if (!ve.isChanged()) continue;
					newKey = ve.getKey();
				}
				createKey.add(newKey);
				createValue.add(newValue);
				createNew.add(Boolean.valueOf(newCreate));
				createDisplay.add(Boolean.valueOf(newDisplay));
				createCode.add(newCode);
				createDispPos.add(Integer.valueOf(newDispPos));
				createUnits.add(Integer.valueOf(newUnits));
			}
			new ApplyChanges(owner, createKey, createValue, createNew, createDisplay, createCode, createDispPos, createUnits, deleteTheseVars);
		}

		/**
		 * Cancel all changes
		 */
//		public void cancelChanges() {}

		private String getUniqueName(String name) {
			boolean nameConflict = true;
			String newName = name;
			int i = 0;
			while (nameConflict) {
				nameConflict = false;
				i++;
				for (VarEntry ve : vars) {
					if (newName.equals(ve.getName())) {
						nameConflict = true;
						newName = name + "_" + i;
						break;
					}
				}
			}
			return newName;
		}

		private static class ApplyChanges extends Job
		{
			private ElectricObject owner;
			private List<Variable.Key> createKey;
			private List<Object> createValue;
			private List<Boolean> createNew;
			private List<Boolean> createDisplay;
			private List<CodeExpression.Code> createCode;
			private List<Integer> createDispPos;
			private List<Integer> createUnits;
			private List<Variable.Key> varsToDelete;

			private ApplyChanges(ElectricObject owner,
				List<Variable.Key> createKey,
				List<Object> createValue,
				List<Boolean> createNew,
				List<Boolean> createDisplay,
				List<CodeExpression.Code> createCode,
				List<Integer> createDispPos,
				List<Integer> createUnits,
				List<Variable.Key> varsToDelete)
			{
				super("Apply Attribute Changes", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
				this.owner = owner;
				this.createKey = createKey;
				this.createValue = createValue;
				this.createNew = createNew;
				this.createDisplay = createDisplay;
				this.createCode = createCode;
				this.createDispPos = createDispPos;
				this.createUnits = createUnits;
				this.varsToDelete = varsToDelete;
				startJob();
			}

            @Override
			public boolean doIt() throws JobException
			{
                EditingPreferences ep = getEditingPreferences();
				// delete variables first
				for (Variable.Key key : varsToDelete)
				{
					if (owner.isParam(key)) {
                        if (owner instanceof Cell) {
                        	CellGroup cg = ((Cell)owner).getCellGroup();
                        	if (cg != null) cg.delParam((Variable.AttrKey)key);
                        } else if (owner instanceof NodeInst)
                            ((NodeInst)owner).delParameter(key);
                    } else {
						owner.delVar(key);
                    }
				}

				// now create new and update existing variables
				for(int i=0; i<createKey.size(); i++)
				{
					Variable.Key key = createKey.get(i);
					Object obj = createValue.get(i);
					boolean makeNew = createNew.get(i).booleanValue();
					boolean display = createDisplay.get(i).booleanValue();
					CodeExpression.Code code = createCode.get(i);
					TextDescriptor.DispPos dispPos = TextDescriptor.DispPos.getShowStylesAt(createDispPos.get(i).intValue());
					TextDescriptor.Unit units = TextDescriptor.Unit.getUnitAt(createUnits.get(i).intValue());
					obj = Variable.withCode(obj, code);

					Variable newVar = null;
					if (makeNew)
					{
						// this is a new variable
						newVar = owner.newVar(key, obj, ep);
					} else
					{
						// update variable
						newVar = owner.updateVar(key, obj, ep);
					}

					if (newVar != null)
					{
						// set/update properties
						assert newVar.getCode() == code;
						TextDescriptor td = null;

						// see if this is a parameter
						if (owner instanceof NodeInst)
						{
							NodeInst ni = (NodeInst)owner;
							if (ni.isCellInstance())
							{
								Cell cell = (Cell)ni.getProto();
								for(Iterator<Variable> it = cell.getParameters(); it.hasNext(); )
								{
									Variable var = it.next();
									if (var.getKey() == key)
									{
										// get text descriptor, adjust for cell center
										td = var.getTextDescriptor();
										double xOff = td.getXOff();
										double yOff = td.getYOff();
										xOff -= cell.getBounds().getCenterX();
										yOff -= cell.getBounds().getCenterY();
										td = td.withOff(xOff, yOff);
									}
								}
							}
						}

						if (td == null) td = newVar.getTextDescriptor();
						td = td.withDisplay(display).withDispPart(dispPos).withUnit(units);
						owner.setTextDescriptor(newVar.getKey(), td);
					}
				}
				return true;
			}

		} // end class ApplyChanges

	} // end class VariableTableModel

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

	private static JComboBox codeComboBox = null;
	private static JComboBox dispComboBox = null;
	private static JComboBox unitComboBox = null;
	private static final String displaynone = "None";
	private JPopupMenu popup;
	private Point popupLocation;
	private ElectricObject owner;

	/**
	 * Create a new Attributes Table
	 */
	public AttributesTable(ElectricObject owner, boolean showCode, boolean showDispPos, boolean showUnits) {
		setElectricObject(owner);

		setGridColor(getBackground());          // hides cell grids
		setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

		VariableTableModel model = new VariableTableModel(showCode, showDispPos, showUnits);
		setModel(model);

		initComboBoxes();

		// set up combo box editors
		if (showCode) {
			TableColumn codeColumn = getColumnModel().getColumn((model.getCodeColumn()));
			if (codeColumn != null)
				codeColumn.setCellEditor(new DefaultCellEditor(codeComboBox));
		}
		if (showDispPos) {
			TableColumn codeColumn = getColumnModel().getColumn((model.getDispColumn()));
			if (codeColumn != null)
				codeColumn.setCellEditor(new DefaultCellEditor(dispComboBox));
		}
		if (showUnits){
			TableColumn codeColumn = getColumnModel().getColumn((model.getUnitsColumn()));
			if (codeColumn != null)
				codeColumn.setCellEditor(new DefaultCellEditor(unitComboBox));
		}

		MouseListener mouseListener = new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				if (e.isShiftDown() || e.isControlDown() || e.isAltDown()) return;

				if (e.isMetaDown()) {
					initPopupMenu();
					popupLocation = new Point(e.getX(), e.getY());
					popup.show(e.getComponent(), e.getX(), e.getY());
				}
			}
		};
		addMouseListener(mouseListener);
		UserInterfaceMain.addDatabaseChangeListener(this);
	}

	private void initComboBoxes() {
		if (codeComboBox == null) {
			codeComboBox = new JComboBox();
			for (Iterator<CodeExpression.Code> it = CodeExpression.Code.getCodes(); it.hasNext(); ) {
				codeComboBox.addItem(it.next());
			}
			codeComboBox.setFont(new Font("Dialog", 0, 11));
		}
		if (dispComboBox == null) {
			dispComboBox = new JComboBox();
			dispComboBox.addItem(displaynone);
			for (Iterator<TextDescriptor.DispPos> it = TextDescriptor.DispPos.getShowStyles(); it.hasNext(); ) {
				dispComboBox.addItem(it.next());
			}
			dispComboBox.setFont(new Font("Dialog", 0, 11));
		}
		if (unitComboBox == null) {
			unitComboBox = new JComboBox();
			for (Iterator<TextDescriptor.Unit> it = TextDescriptor.Unit.getUnits(); it.hasNext(); ) {
				unitComboBox.addItem(it.next());
			}
			unitComboBox.setFont(new Font("Dialog", 0, 11));
		}
	}

	private void initPopupMenu() {
		popup = new JPopupMenu();
		JMenuItem m;
		m = new JMenuItem("New Attr");
		m.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) { newVar(); }
		});
		popup.add(m);
//		m = new JMenuItem("Edit Cell");
//		m.addActionListener(new ActionListener() {
//			public void actionPerformed(ActionEvent e) { editCell(popup.getLocation()); }
//		});
//		popup.add(m);
		m = new JMenuItem("Duplicate Attr");
		m.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) { duplicateVar(popupLocation); }
		});
		popup.add(m);
		m = new JMenuItem("Delete Attr");
		m.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) { deleteVar(popupLocation); }
		});
		popup.add(m);
		JMenu showMenu = new JMenu("Show...");
		JCheckBoxMenuItem cb;
		VariableTableModel model = (VariableTableModel)getModel();
		cb = new JCheckBoxMenuItem("Code", model.showCode);
		cb.addActionListener(new ActionListener() {
		   public void actionPerformed(ActionEvent e) { toggleShowCode(); }
		});
		showMenu.add(cb);
		cb = new JCheckBoxMenuItem("Display", model.showDispPos);
		cb.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) { toggleShowDisp(); }
		});
		showMenu.add(cb);
		cb = new JCheckBoxMenuItem("Units", model.showUnits);
		cb.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) { toggleShowUnits(); }
		});
		showMenu.add(cb);
		popup.add(showMenu);
	}

	/**
	 * Create a new Variable
	 */
	private void newVar() {
		VariableTableModel model = (VariableTableModel)getModel();
		model.newVar(owner);
	}

	/**
	 * Duplicate the Variable in the row pointed to by location
	 * @param location
	 */
	private void duplicateVar(Point location) {
		int row = rowAtPoint(location);
		VariableTableModel model = (VariableTableModel)getModel();
		model.duplicateVar(row);
	}

	/**
	 * Delete the Variable in the row pointed to by location
	 * @param location
	 */
	private void deleteVar(Point location) {
		int row = rowAtPoint(location);
		VariableTableModel model = (VariableTableModel)getModel();
		model.deleteVar(row);
	}

	/**
	 * Applies all changes made to attributes to database
	 */
	public void applyChanges() {
		VariableTableModel model = (VariableTableModel)getModel();
		// clean up if a cell is currently being edited
		if (isEditing()) {
			int row = getEditingRow();
			int col = getEditingColumn();
			TableCellEditor editor = getCellEditor(row, col);
			editor.stopCellEditing();
		}
		model.applyChanges();
	}

	/**
	 * Method to handle the "Cancel" button in attributes.
	 */
	public void cancelChanges() {
		// clean up if a cell is currently being edited
		if (isEditing()) {
			int row = getEditingRow();
			int col = getEditingColumn();
			TableCellEditor editor = getCellEditor(row, col);
			editor.cancelCellEditing();
		}
		// revert back to database values
		setElectricObject(owner);
	}

	private void toggleShowCode() {
		VariableTableModel model = (VariableTableModel)getModel();
		model.setShowCode(!model.showCode);
		updateEditors();
	}

	private void toggleShowDisp() {
		VariableTableModel model = (VariableTableModel)getModel();
		model.setShowDisp(!model.showDispPos);
		updateEditors();
	}

	private void toggleShowUnits() {
		VariableTableModel model = (VariableTableModel)getModel();
		model.setShowUnits(!model.showUnits);
		updateEditors();
	}

	private void updateEditors() {
		VariableTableModel model = (VariableTableModel)getModel();
		int codeCol = model.getCodeColumn();
		if (codeCol != -1) {
			TableColumn codeColumn = getColumnModel().getColumn(codeCol);
			if (codeColumn != null)
				codeColumn.setCellEditor(new DefaultCellEditor(codeComboBox));
		}
		int dispCol = model.getDispColumn();
		if (dispCol != -1) {
			TableColumn codeColumn = getColumnModel().getColumn(dispCol);
			if (codeColumn != null)
				codeColumn.setCellEditor(new DefaultCellEditor(dispComboBox));
		}
		int unitsCol = model.getUnitsColumn();
		if (unitsCol != -1) {
			TableColumn codeColumn = getColumnModel().getColumn(unitsCol);
			if (codeColumn != null)
				codeColumn.setCellEditor(new DefaultCellEditor(unitComboBox));
		}
	}

	/**
	 * Set the ElectricObject whose Variables will be shown
	 * @param eobj
	 */
	public void setElectricObject(ElectricObject eobj) {
		if (owner == eobj) return;
		// clear old vars
		clearVariables();
		// add new vars
		if (eobj != null) {
			List<Variable> vars = new ArrayList<Variable>();
			if (eobj instanceof NodeInst) {
				for (Iterator<Variable> it = ((NodeInst)eobj).getParameters(); it.hasNext(); ) {
					// only add attributes
					Variable param = it.next();
					vars.add(param);
				}
				for (Iterator<Variable> it = eobj.getVariables(); it.hasNext(); ) {
					// only add attributes
					Variable var = it.next();
					if (var.isAttribute() && !eobj.isParam(var.getKey()))
						vars.add(var);
				}
			} else {
				for (Iterator<Variable> it = eobj.getVariables(); it.hasNext(); ) {
					// only add attributes
					Variable var = it.next();
					if (var.isAttribute())
						vars.add(var);
				}
			}
			// sort vars by name
			//Collections.sort(vars, new Attributes.VariableNameSort());
			((VariableTableModel)getModel()).setVars(eobj, vars);
		}
		owner = eobj;
	}

	/**
	 * Clear all variables from the table
	 */
	private void clearVariables() {
		((VariableTableModel)getModel()).clearVariables();
	}

	public void databaseChanged(DatabaseChangeEvent e) {
		// reload vars
		ElectricObject eobj = owner;
		setElectricObject(null);
		setElectricObject(eobj);
	}

}
