package lu.tudor.santec.gecamed.formeditor.gui.component;

import java.awt.BorderLayout;
import java.awt.Label;
import java.awt.TextComponent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.text.JTextComponent;

import lu.tudor.santec.gecamed.core.gui.GECAMedIconNames;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.widgets.NamedComponent;
import lu.tudor.santec.gecamed.formeditor.gui.FormEditorModule;
import lu.tudor.santec.gecamed.formeditor.gui.controller.AbstractListener;
import lu.tudor.santec.gecamed.formeditor.gui.controller.FormController;
import lu.tudor.santec.gecamed.formeditor.gui.model.ComboBoxElement;
import lu.tudor.santec.gecamed.formeditor.gui.model.FormEditorModel;
import lu.tudor.santec.gecamed.formeditor.gui.model.FormModel;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;
import org.jdom.Element;

import au.com.bytecode.opencsv.CSVReader;
import au.com.bytecode.opencsv.CSVWriter;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;


public class EditableTable extends EditableComponent
{
	private static final long	serialVersionUID			= 1L;
	
	private static final int	LISTENER_METHOD_SIZE		= 0;
	// private static final int TABLE_MODEL_LISTENER =
	// SUPER_LISTENER_METHOD_SIZE;
	
	public static final String	COLUMN_SIZE					= "columnSize";
	public static final String	ROW_SIZE					= "rowSize";
	public static final String	SELECTION_MODE				= "selectionMode";
	public static final String	COLUMN_SELECTION_ALLOWED	= "columnSelectionAllowed";
	public static final String	ROW_SELECTION_ALLOWED		= "rowSelectionAllowed";
	public static final String	TABLE_IS_FIX				= "fixTable";
	public static final String	COLUMN_NAMES				= "columnNames";
	
	public static final char	SEPARATOR_CHAR				= ';';
	public static final char	QUOTE_CHAR					= '"';
	public static final char	ESCAPE_CHAR					= '\\';
	public static final String	LINE_END					= "\n";
	
	private static final String	XML_TABLE					= "table";
	private static final String	XML_HEADER					= "head";
	private static final String	XML_BODY					= "body";
	private static final String	XML_ROW						= "row";
	private static final String	XML_HEADER_CELL				= "hcell";
	private static final String	XML_CELL					= "cell";
	private static final String	XML_CONTENT					= "content";
	
	protected DefaultTableModel	tableModel;
	
	protected int				tableColumn;
	protected int				tableRow;
	
	private Boolean				tableIsFix					= false;
	
	
	public EditableTable()
	{
		super();
		tableModel = new DefaultTableModel();
		super.component = new JTable(tableModel)
		{
			private static final long	serialVersionUID	= 1L;
			
			
			// used for scripting as there are problems with the usage of Class
			@SuppressWarnings("unused")
			public TableCellRenderer getDefaultRenderer()
			{
				return super.getDefaultRenderer(String.class);
			}
		};
		
		tableModel.setColumnCount(3);
		tableModel.setRowCount(3);
		
		((JTable) component).setColumnSelectionAllowed(false);
		((JTable) component).setRowSelectionAllowed(true);
	}
	
	
	@Override
	public void init(CellConstraints constraints, FormEditorModel formModel, byte formModelType)
	{
		super.init(constraints, formModel, formModelType);
		this.remove(component);
		JScrollPane scrollPane = new JScrollPane(component);
		scrollPane.getVerticalScrollBar().setUnitIncrement(16);
		scrollPane.setOpaque(false);
		scrollPane.getViewport().setOpaque(false);
		scrollPane.setBorder(BorderFactory.createEmptyBorder());
		this.add(scrollPane, BorderLayout.CENTER);
		
		((JTable) component).setDefaultRenderer(String.class, new EditableCellRenderer());
	}
	
	
	@Override
	protected void addListenerMethodNames() {}
	
	
	@Override
	public JComponent copyComponent(FormModel model)
	{
		JTable table = (JTable) this.component;
		
		// ADD DEFAULT LISTENER (to react on the script)
		FormController controller = new FormController(model);
		addDefaultListener(table, controller);
		
		// ADD THE TABLE LISTENER
		AbstractListener tableListener = this.createTableListener(table);
		table.addKeyListener(tableListener);
		if (!tableIsFix)
			table.addMouseListener(tableListener);
		
		return table;
	}
	
	
	@Override
	public JPanel getAddablePanel(JComponent c)
	{
		JScrollPane sp = new JScrollPane(c);
		sp.getVerticalScrollBar().setUnitIncrement(16);
		sp.setOpaque(false);
		sp.setOpaque(false);
		sp.getViewport().setOpaque(false);
		sp.setBorder(BorderFactory.createEmptyBorder());
		return super.getAddablePanel(sp);
	}
	
	
	@Override
	public String getComponentType()
	{
		return "table";
	}
	
	
	@Override
	protected int getListenerMethodSize()
	{
		return LISTENER_METHOD_SIZE;
	}
	
	
	@Override
	public String getStructure()
	{
		Properties structure = new Properties();
		JTable table = (JTable) component;
		
		structure.setProperty(SELECTION_MODE, String.valueOf(table.getSelectionModel().getSelectionMode()));
		structure.setProperty(COLUMN_SELECTION_ALLOWED, String.valueOf(table.getColumnSelectionAllowed()));
		structure.setProperty(ROW_SELECTION_ALLOWED, String.valueOf(table.getRowSelectionAllowed()));
		structure.setProperty(TABLE_IS_FIX, String.valueOf(tableIsFix));
		
		StringWriter writer = new StringWriter();
		try
		{
			structure.store(writer, "");
		}
		catch (IOException e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		
		return writer.toString();
	}
	
	
	@Override
	public void setStructure(String text)
	{
		/* ======================================== */
		// System.out.println("  =====  SETStructure:  =====  \n"
		// + text + "  ===========================");
		StringReader reader = new StringReader(text);
		Properties structure = new Properties();
		try
		{
			structure.load(reader);
		}
		catch (IOException e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		
		JTable table = (JTable) component;
		
		String selectionMode = structure.getProperty(SELECTION_MODE);
		table.setSelectionMode(selectionMode != null ? Integer.parseInt(selectionMode) : ListSelectionModel.SINGLE_SELECTION);
		table.setColumnSelectionAllowed(Boolean.parseBoolean(structure.getProperty(COLUMN_SELECTION_ALLOWED)));
		table.setRowSelectionAllowed(Boolean.parseBoolean(structure.getProperty(ROW_SELECTION_ALLOWED)));
		tableIsFix = Boolean.parseBoolean(structure.getProperty(TABLE_IS_FIX));
		
		if (structure.getProperty(ROW_SIZE) != null)
		{
			/* ---------------------------------------- */
			// THE OLD WAY TO LOAD THE STRUCTURE
			/* ---------------------------------------- */
			// column names
			Vector<String> columnNames = new Vector<String>();
			String colNames = structure.getProperty(COLUMN_NAMES);
			if (colNames != null)
			{
				for (String name : colNames.split("\n"))
				{
					columnNames.addElement(name);
				}
			}
			else
			{
				for (int index = 0; index < table.getColumnCount(); index++)
				{
					columnNames.addElement(String.valueOf((char) ('A' + index)));
				}
			}
			
			tableModel.setDataVector(tableModel.getDataVector(), columnNames);
			
			tableModel.setRowCount(Integer.parseInt(structure.getProperty(ROW_SIZE)));
			tableModel.setColumnCount(Integer.parseInt(structure.getProperty(COLUMN_SIZE)));
			
		}
		
		table.validate();
	}
	
	
	@Override
	public String getText()
	{
		StringWriter writer = new StringWriter();
		CSVWriter csvWriter = new CSVWriter(writer, SEPARATOR_CHAR, QUOTE_CHAR, ESCAPE_CHAR, LINE_END);
		
		ArrayList<String[]> csvData = new ArrayList<String[]>();
		
		// read the column names
		String[] columnNames = new String[tableModel.getColumnCount()];
		for (int columnIndex = 0; columnIndex < tableModel.getColumnCount(); columnIndex++)
		{
			columnNames[columnIndex] = (String) ((JTable) component).getColumnModel().getColumn(columnIndex).getHeaderValue();
		}
		
		// write the column names into the first line of the CSV-file
		csvData.add(columnNames);
		
		Vector<?> data = tableModel.getDataVector();
		Vector<?> rowData;
		String[] array;
		for (int rowIndex = 0; rowIndex < data.size(); rowIndex++)
		{
			rowData = (Vector<?>) data.get(rowIndex);
			array = new String[rowData.size()];
			for (int col = 0; col < array.length; col++)
				array[col] = String.valueOf(rowData.get(col));
			csvData.add(array);
		}
		
		// write all the data of the vector into the CSV-file
		csvWriter.writeAll(csvData);
		
		return writer.toString();
	}
	
	
	@Override
	public void setText(String text)
	{
		try
		{
			// read the value
			CSVReader csvReader = new CSVReader(new StringReader(text), SEPARATOR_CHAR, QUOTE_CHAR, ESCAPE_CHAR, 0, false, false);
			
			List<String[]> csvData = csvReader.readAll();
			
			if (csvData.size() <= 1 || csvData.get(0).length <= 1)
			{
				throw new IOException();
			}
			
			// read the column names, which are in the first row of the CSV-file
			Vector<String> columnNames = new Vector<String>();
			
			String[] csvRowData = csvData.get(0);
			for (int column = 0; column < csvRowData.length; column++)
			{
				columnNames.add(csvRowData[column]);
			}
			
			
			// set the value
			Vector<Vector<String>> tableData = new Vector<Vector<String>>();
			for (int row = 1; row < csvData.size(); row++)
			{
				csvRowData = csvData.get(row);
				Vector<String> tableRowData = new Vector<String>();
				
				for (int column = 0; column < csvRowData.length; column++)
				{
					tableRowData.add(csvRowData[column]);
				}
				tableData.add(tableRowData);
			}
			
			// set the data of the table model
			tableModel.setDataVector(tableData, columnNames);
			// ((JTable)component).setModel(new DefaultTableModel(tableData,
			// columnNames));
		}
		catch (IOException ex)
		{
			/* **************************************** */
			// the old way to load the table values
			/* **************************************** */
			// if the table is still stored in the old way, try this ...
			
			
			/* ---------------------------------------- */
			// THE OLD SET STRUCTURE
			/* ---------------------------------------- */
			Properties structure = new Properties();
			try
			{
				structure.load(new StringReader(text));
			}
			catch (IOException e)
			{
				logger.log(Level.ERROR, e.getMessage(), e);
			}
			
			Enumeration<?> keys = (Enumeration<?>) structure.propertyNames();
			while (keys.hasMoreElements())
			{
				String key = (String) keys.nextElement();
				String value = structure.getProperty(key, "");
				
				try
				{
					int col = Integer.parseInt(key.split(":")[0]);
					int row = Integer.parseInt(key.split(":")[1]);
					tableModel.setValueAt(value, row, col);
					// ((JTable)component).setValueAt(value, row, col);
				}
				catch (NumberFormatException nfe)
				{
				}
			}
		}
	}
	
	
	@Override
	public Element getDataAsXML()
	{
		Element data = new Element(getKey());
		// Element text = new Element(XML_ELEMENT_TEXT);
		//
		// text.setText("");
		// data.addContent(text);
		
		data.addContent(createTableStructure((JTable) component));
		
		addDefaultProperties(data);
		return data;
	}
	
	
	public static Element createTableStructure(JTable jtable)
	{
		TableModel model = jtable.getModel();
		int rowCount = model.getRowCount();
		int columnCount = model.getColumnCount();
		TableColumnModel columnModel = jtable.getTableHeader().getColumnModel();
		Object cellObject;
		
		Element table = new Element(XML_TABLE);
		Element header = new Element(XML_HEADER);
		Element body = new Element(XML_BODY);
		Element row;
		Element column;
		Element content;
		
		// fill the head element
		row = new Element(XML_ROW);
		for (int index = 0; index < columnModel.getColumnCount(); index++)
		{
			column = new Element(XML_HEADER_CELL);
			content = new Element(XML_CONTENT);
			cellObject = columnModel.getColumn(index).getHeaderValue();
			
			content.setText(getTextOfCellObject(cellObject));
			
			column.addContent(content);
			row.addContent(column);
		}
		header.addContent(row);
		
		// fill the table data (rows and columns)
		for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
		{
			row = new Element(XML_ROW);
			for (int colIndex = 0; colIndex < columnCount; colIndex++)
			{
				column = new Element(XML_CELL);
				content = new Element(XML_CONTENT);
				cellObject = model.getValueAt(rowIndex, colIndex);
				
				content.setText(getTextOfCellObject(cellObject));
				
				column.addContent(content);
				row.addContent(column);
			}
			body.addContent(row);
		}
		
		table.addContent(header);
		table.addContent(body);
		
		return table;
	}
	
	
	private static String getTextOfCellObject(Object cellObject)
	{
		if (cellObject == null)
			return "";
		else if (cellObject instanceof String)
			return (String) cellObject;
		else if (cellObject instanceof JLabel)
			return ((JLabel) cellObject).getText();
		else if (cellObject instanceof Label)
			return ((Label) cellObject).getText();
		else if (cellObject instanceof JTextComponent)
			return ((JTextComponent) cellObject).getText();
		else if (cellObject instanceof TextComponent)
			return ((TextComponent) cellObject).getText();
		else
		{
			logger.log(Level.WARN, cellObject.getClass() + " is not supported to get text of table cell!");
			return cellObject.toString();
		}
	}
	
	
	@Override
	public String getTypeTranslation()
	{
		return Translatrix.getTranslationString("formeditor.add_table");
	}
	
	
	@Override
	public DefaultOptionPanel createOptionPanel()
	{
		JTable table = (JTable) component;
		DefaultOptionPanel panel = super.createOptionPanel();
		
		panel.getCaptionTextComponent().setVisible(false);
		
		CellConstraints cc = new CellConstraints();
		
		// TABLE COLUMN PANEL
		final JTextField tableColField = new JTextField();
		tableColField.setEditable(false);
		tableColField.setOpaque(false);
		
		JSpinner tableColSizeSpinner = new JSpinner(new SpinnerNumberModel(tableModel.getColumnCount(), 1, Integer.MAX_VALUE, 1));
		tableColSizeSpinner.addChangeListener(new ChangeListener()
		{
			public void stateChanged(ChangeEvent e)
			{
				JSpinner spinner = (JSpinner) e.getSource();
				tableModel.setColumnCount((Integer) spinner.getValue());
			}
		});
		
		JPanel columnPanel = new JPanel(new FormLayout("f:75px, 5px, f:75px", "f:p"));
		columnPanel.setOpaque(false);
		columnPanel.add(new NamedComponent(Translatrix.getTranslationString("formeditor.table_column"), tableColField), cc.xy(1, 1));
		columnPanel.add(new NamedComponent(Translatrix.getTranslationString("formeditor.column_width"), tableColSizeSpinner), cc.xy(3, 1));
		panel.appendRow(columnPanel);
		
		// TABLE ROW PANEL
		final JTextField tableRowField = new JTextField();
		tableRowField.setEditable(false);
		tableRowField.setOpaque(false);
		
		JSpinner tableRowSizeSpinner = new JSpinner(new SpinnerNumberModel(tableModel.getRowCount(), 1, Integer.MAX_VALUE, 1));
		tableRowSizeSpinner.addChangeListener(new ChangeListener()
		{
			public void stateChanged(ChangeEvent e)
			{
				JSpinner spinner = (JSpinner) e.getSource();
				tableModel.setRowCount((Integer) spinner.getValue());
			}
		});
		
		JPanel rowPanel = new JPanel(new FormLayout("f:75px, 5px, f:75px", "f:p"));
		rowPanel.setOpaque(false);
		rowPanel.add(new NamedComponent(Translatrix.getTranslationString("formeditor.table_row"), tableRowField), cc.xy(1, 1));
		rowPanel.add(new NamedComponent(Translatrix.getTranslationString("formeditor.row_height"), tableRowSizeSpinner), cc.xy(3, 1));
		panel.appendRow(rowPanel);
		
		// COLUMN NAME
		final JTextField columnNameField = new JTextField();
		
		columnNameField.addKeyListener(new KeyListener()
		{
			public void keyTyped(KeyEvent e) {}
			
			
			public void keyPressed(KeyEvent e) {}
			
			
			public void keyReleased(KeyEvent e)
			{
				JTable table = (JTable) component;
				JTextField tf = (JTextField) e.getSource();
				
				table.getColumnModel().getColumn(tableColumn).setHeaderValue(tf.getText());
				table.getTableHeader().repaint();
			}
		});
		
		panel.appendRow(new NamedComponent(Translatrix.getTranslationString("formeditor.column_name"), columnNameField));
		
		// CELL VALUE
		final JTextField cellValueField = new JTextField();
		cellValueField.addKeyListener(new KeyListener()
		{
			public void keyTyped(KeyEvent e) {}
			
			
			public void keyPressed(KeyEvent e) {}
			
			
			public void keyReleased(KeyEvent e)
			{
				tableModel.setValueAt(((JTextField) e.getSource()).getText(), ((JTable) component).getSelectedRow(), tableColumn);
			}
		});
		
		panel.appendRow(new NamedComponent(Translatrix.getTranslationString("formeditor.cell_value"), cellValueField));
		
		// MULTISELECTION
		ListSelectionModel selectionModel = table.getSelectionModel();
		int selectionMode = selectionModel.getSelectionMode();
		
		Vector<ComboBoxElement<Integer>> selectionModeModel = new Vector<ComboBoxElement<Integer>>();
		selectionModeModel.addElement(new ComboBoxElement<Integer>(Translatrix.getTranslationString("formeditor.multi_interval_selection"), ListSelectionModel.MULTIPLE_INTERVAL_SELECTION));
		selectionModeModel.addElement(new ComboBoxElement<Integer>(Translatrix.getTranslationString("formeditor.single_interval_selection"), ListSelectionModel.SINGLE_INTERVAL_SELECTION));
		selectionModeModel.addElement(new ComboBoxElement<Integer>(Translatrix.getTranslationString("formeditor.single_selection"), ListSelectionModel.SINGLE_SELECTION));
		
		JComboBox selectionModeBox = new JComboBox(selectionModeModel);
		for (int index = 0; index < selectionModeModel.size(); index++)
		{
			if (selectionModeModel.get(index).getValue().equals(selectionMode))
				selectionModeBox.setSelectedIndex(index);
		}
		selectionModeBox.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				JComboBox cb = (JComboBox) e.getSource();
				int selectionMode = (Integer) ((ComboBoxElement<?>) cb.getSelectedItem()).getValue();
				((JTable) component).setSelectionMode(selectionMode);
			}
		});
		
		panel.appendRow(new NamedComponent(Translatrix.getTranslationString("formeditor.selection_mode"), selectionModeBox));
		
		// ROW SELECTION
		JCheckBox rowSelectionBox = new JCheckBox(Translatrix.getTranslationString("formeditor.row_selection"), table.getRowSelectionAllowed());
		rowSelectionBox.setOpaque(false);
		rowSelectionBox.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				JTable table = (JTable) component;
				JCheckBox box = (JCheckBox) e.getSource();
				table.setRowSelectionAllowed(box.isSelected());
			}
		});
		
		panel.appendRow(rowSelectionBox);
		
		// COLUMN SELECTION
		JCheckBox colSelectionBox = new JCheckBox(Translatrix.getTranslationString("formeditor.column_selection"), table.getColumnSelectionAllowed());
		colSelectionBox.setOpaque(false);
		colSelectionBox.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				JTable table = (JTable) component;
				JCheckBox box = (JCheckBox) e.getSource();
				table.setColumnSelectionAllowed(box.isSelected());
			}
		});
		
		panel.appendRow(colSelectionBox);
		
		// TABLE IS FIX
		JCheckBox tableIsFixBox = new JCheckBox(Translatrix.getTranslationString("formeditor.table_fix"), tableIsFix);
		tableIsFixBox.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				tableIsFix = ((JCheckBox) e.getSource()).isSelected();
			}
		});
		tableIsFixBox.setOpaque(false);
		
		panel.appendRow(tableIsFixBox);
		
		
		// add the table listener
		AbstractListener tableListener = new AbstractListener()
		{
			public void selectionChanged()
			{
				JTable table = (JTable) component;
				tableColumn = table.getSelectedColumn();
				tableRow = table.getSelectedRow();
				if (tableColumn < 0 || tableRow < 0)
				{
					// nothing is selected, set the text to empty string ...
					tableColField.setText("");
					tableRowField.setText("");
					cellValueField.setText("");
					columnNameField.setText("");
					
					// ... and make them uneditable
					cellValueField.setEditable(false);
					columnNameField.setEditable(false);
				}
				else
				{
					// set the index of the selected column
					tableColField.setText(String.valueOf(tableColumn));
					// set the index of the selected row
					tableRowField.setText(String.valueOf(tableRow));
					// set the value of this cell
					cellValueField.setText((String) table.getValueAt(tableRow, tableColumn));
					// set the column name
					columnNameField.setText((String) table.getColumnModel().getColumn(tableColumn).getHeaderValue());
					
					// make the text fields editable
					cellValueField.setEditable(true);
					columnNameField.setEditable(true);
				}
			}
			
			
			@Override
			public void mousePressed(MouseEvent e)
			{
				selectionChanged();
			}
			
			
			@Override
			public void keyReleased(KeyEvent e)
			{
				selectionChanged();
			}
		};
		tableListener.mousePressed(null);
		table.addMouseListener(tableListener);
		table.addKeyListener(tableListener);
		
		return panel;
	}
	
	
	@Override
	public DefaultPropertyPanel createPropertiesPanel()
	{
		DefaultPropertyPanel panel = super.createPropertiesPanel();
		
		panel.getBackgroundColorComponent().setVisible(false);
		panel.getPaintBackgroundBox().setEnabled(false);
		panel.getPaintBackgroundBox().setSelected(true);
		
		return panel;
	}
	
	
	private AbstractListener createTableListener(JTable table)
	{
		AbstractListener tableListener = new AbstractListener(table)
		{
			private JPopupMenu	popupMenu	= createPopupMenu();
			
			
			@SuppressWarnings("unchecked")
			private void addColumn(int atColumn)
			{
				JTable table = (JTable) get();
				
				if (atColumn >= 0)
				{
					Vector<Vector<String>> data = (Vector<Vector<String>>) ((DefaultTableModel) table.getModel()).getDataVector();
					((DefaultTableModel) table.getModel()).addColumn("");
					
					// switch
					for (int index = 0; index < data.size(); index++)
					{
						Vector<String> columnData = data.elementAt(index);
						columnData.add(atColumn, "");
						columnData.removeElementAt(columnData.size() - 1);
					}
					
					table.validate();
				}
			}
			
			
			@SuppressWarnings("unchecked")
			private void addRow(int atRow)
			{
				JTable table = (JTable) get();
				
				if (atRow >= 0)
				{
					Vector<Vector<String>> data = ((DefaultTableModel) table.getModel()).getDataVector();
					
					// define the data of the new row
					Vector<String> emptyRowData = new Vector<String>();
					for (int index = 0; index < table.getColumnCount(); index++)
					{
						emptyRowData.add("");
					}
					
					// enlarge the table, so there is enough space for the new
					// row
					((DefaultTableModel) table.getModel()).setRowCount(table.getRowCount() + 1);
					// add the empty data into the data vector
					data.add(atRow, emptyRowData);
					// remove the needless empty data at the end
					data.remove(data.size() - 1);
					
					table.validate();
				}
			}
			
			
			private JPopupMenu createPopupMenu()
			{
				if (popupMenu == null)
				{
					popupMenu = new JPopupMenu();
					
					// menu item - add column before selection
					popupMenu.add(new JMenuItem(new AbstractAction(Translatrix.getTranslationString("formeditor.add_column_before"), FormEditorModule.getMiniIcon("add_column.png"))
					{
						private static final long	serialVersionUID	= 1L;
						
						
						public void actionPerformed(ActionEvent e)
						{
							JTable table = (JTable) get();
							addColumn(table.getSelectedColumn());
						}
					}));
					
					// menu item - add column after selection
					popupMenu.add(new JMenuItem(new AbstractAction(Translatrix.getTranslationString("formeditor.add_column_after"), FormEditorModule.getMiniIcon("add_column.png"))
					{
						private static final long	serialVersionUID	= 1L;
						
						
						public void actionPerformed(ActionEvent e)
						{
							JTable table = (JTable) get();
							addColumn(table.getSelectedColumn() + 1);
						}
					}));
					
					// menu item - add row before selection
					popupMenu.add(new JMenuItem(new AbstractAction(Translatrix.getTranslationString("formeditor.add_row_before"), GECAMedModule.getMiniIcon(GECAMedIconNames.ADD_LINE))
					{
						private static final long	serialVersionUID	= 1L;
						
						
						public void actionPerformed(ActionEvent e)
						{
							JTable table = (JTable) get();
							addRow(table.getSelectedRow());
						}
					}));
					
					// menu item - add row after selection
					popupMenu.add(new JMenuItem(new AbstractAction(Translatrix.getTranslationString("formeditor.add_row_after"), GECAMedModule.getMiniIcon(GECAMedIconNames.ADD_LINE))
					{
						private static final long	serialVersionUID	= 1L;
						
						
						public void actionPerformed(ActionEvent e)
						{
							JTable table = (JTable) get();
							addRow(table.getSelectedRow() + 1);
						}
					}));
					
					popupMenu.addSeparator();
					
					// menu item - remove selected columns
					popupMenu.add(new JMenuItem(new AbstractAction(Translatrix.getTranslationString("formeditor.remove_column"), FormEditorModule.getMiniIcon("remove_column.png"))
					{
						private static final long	serialVersionUID	= 1L;
						
						
						@SuppressWarnings("unchecked")
						public void actionPerformed(ActionEvent e)
						{
							JTable table = (JTable) get();
							DefaultTableModel model = (DefaultTableModel) table.getModel();
							Vector<Vector<String>> data = model.getDataVector();
							
							int[] cols = table.getSelectedColumns();
							// must be from back to front, because the indexes
							// change after deleting
							synchronized (table)
							{
								for (int index = cols.length - 1; index >= 0; index--)
								{
									// there is no method to delete a specified
									// column
									// -> delete it manually
									for (int j = 0; j < data.size(); j++)
									{
										data.elementAt(j).removeElementAt(cols[index]);
									}
								}
								/*
								 * tell the view the number of columns,
								 * otherwise it expects more and will throw an
								 * exception
								 */
								model.setColumnCount(data.elementAt(0).size());
							}
							table.validate();
						}
					}));
					
					// menu item - remove selected rows
					popupMenu.add(new JMenuItem(new AbstractAction(Translatrix.getTranslationString("formeditor.remove_row"), GECAMedModule.getMiniIcon(GECAMedIconNames.REMOVE_LINE))
					{
						private static final long	serialVersionUID	= 1L;
						
						
						public void actionPerformed(ActionEvent e)
						{
							JTable table = (JTable) get();
							DefaultTableModel model = (DefaultTableModel) table.getModel();
							
							int[] rows = table.getSelectedRows();
							// must be from back to front, because the indexes
							// change after deleting
							for (int index = rows.length - 1; index >= 0; index--)
							{
								model.removeRow(rows[index]);
							}
						}
					}));
				}
				
				return popupMenu;
			}
			
			
			@Override
			public void keyTyped(KeyEvent e) {}
			
			
			/*
			 * The key event to delete the input of the selected tabs
			 */
			@Override
			public void keyReleased(KeyEvent e)
			{
				JTable table = (JTable) e.getSource();
				
				if (e.getKeyCode() == KeyEvent.VK_DELETE)
				{
					/*
					 * after pressing delete, a selected cell will be edited,
					 * where the content still stands in. -> cancel editing, to
					 * set the value of the model
					 */
					table.editingCanceled(new ChangeEvent(table));
					
					// if delete is pressed, delete the content of the marked
					// cells
					int[] selectedColumns = table.getSelectedColumns();
					int[] selectedRows = table.getSelectedRows();
					
					// delete the content of the selected cells
					for (int i = 0; i < selectedColumns.length; i++)
					{
						for (int j = 0; j < selectedRows.length; j++)
						{
							table.setValueAt(null, selectedRows[j], selectedColumns[i]);
						}
					}
					// table.validate();
				}
			}
			
			
			/*
			 * The key event that adds a additional row, if tab is pressed in
			 * the last right down cell
			 */
			@Override
			public void keyPressed(KeyEvent e)
			{
				if (tableIsFix)
				{
					return;
				}
				
				JTable table = (JTable) e.getSource();
				
				if (e.getKeyCode() == KeyEvent.VK_TAB && table.getColumnCount() - 1 == table.getSelectedColumn() && table.getRowCount() - 1 == table.getSelectedRow())
				{
					// if tab is pressed in the last down right cell, add a new
					// row
					((DefaultTableModel) table.getModel()).setRowCount(table.getRowCount() + 1);
				}
			}
			
			
			/*
			 * The mouse event that opens a popup menu to add and remove columns
			 * and rows
			 */
			@Override
			public void mousePressed(MouseEvent e)
			{
				showPopup(e);
			}
			
			
			@Override
			public void mouseReleased(MouseEvent e)
			{
				showPopup(e);
			}
			
			
			@Override
			public void mouseClicked(MouseEvent e)
			{
				showPopup(e);
			}
			
			
			private void showPopup(MouseEvent e)
			{
				if (e.isPopupTrigger())
				{
					JTable table = (JTable) e.getSource();
					popupMenu.show(table, e.getX(), e.getY());
				}
			}
		};
		
		return tableListener;
	}
}
