Files
secondo/Javagui/viewer/update2/gui/RelationPanel.java

1201 lines
32 KiB
Java
Raw Permalink Normal View History

2026-01-23 17:03:45 +08:00
//This file is part of SECONDO.
//Copyright (C) 2004, University in Hagen, Department of Computer Science,
//Database Systems for New Applications.
//SECONDO 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 2 of the License, or
//(at your option) any later version.
//SECONDO 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 SECONDO; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package viewer.update2.gui;
import gui.SecondoObject;
import gui.idmanager.ID;
import gui.idmanager.IDManager;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.InputEvent;
import java.awt.event.InputMethodListener;
import java.awt.event.InputMethodEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.StringTokenizer;
import javax.swing.AbstractAction;
import javax.swing.BoxLayout;
import javax.swing.CellEditor;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.ToolTipManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.text.BadLocationException;
import sj.lang.ListExpr;
import sj.lang.ServerErrorCodes;
import tools.Reporter;
import viewer.update.CommandExecuter;
import viewer.update2.*;
/**
* Panel that contains a table used to display one relation sequentially,
* i.e. as a sequence of triples (tuple ID, attribute name, attribute value).
*/
public class RelationPanel extends JPanel implements PropertyChangeListener
{
// relation
private String name;
private Relation relation;
private Relation insertRelation;
// display of loaded relation
private JTable relTable;
private JScrollPane relScroll;
// insert function
private JSplitPane splitPane;
private JTable insertTable;
private JScrollPane insertScroll;
// table cell
private ValueTableCellRenderer tableCellRenderer;
private ValueTableCellEditor tableCellEditor;
// saves the old value of the currently edited cell, when in edit mode
private String oldEditCellValue;
// search function
private boolean searchActive;
private JButton search;
private JButton previous;
private JButton previousFast;
private JButton next;
private JButton nextFast;
private JCheckBox chkCaseSensitive;
private JLabel searchResultLabel;
private JTextField searchField;
// replace function
private JPanel replacePanel;
private JButton replace;
private JButton print;
private JButton replaceAll;
private JTextField replaceField;
/**
* Builds a panel to display one relation
*/
public RelationPanel(String pRelationName, UpdateViewerController pController)
{
this.name = pRelationName;
this.searchActive = false;
// tables
this.relScroll = new JScrollPane();
this.insertScroll = new JScrollPane();
this.splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
this.tableCellRenderer = new ValueTableCellRenderer();
this.tableCellEditor = new ValueTableCellEditor();
// search panel
JPanel searchPanel = new JPanel();
searchPanel.setLayout(new FlowLayout());
this.searchField = new JTextField(15);
searchPanel.add(this.searchField);
this.search = new JButton(UpdateViewerController.CMD_SEARCH);
this.search.addActionListener(pController);
searchPanel.add(this.search);
this.chkCaseSensitive = new JCheckBox("case-sensitive");
//this.chkCaseSensitive.addItemListener(pController);
searchPanel.add(this.chkCaseSensitive);
this.previousFast = new JButton(this.createIcon("res/TOFRONT.gif"));
this.previousFast.addActionListener(pController);
this.previousFast.setActionCommand(UpdateViewerController.CMD_FIRST);
this.previousFast.setToolTipText("Go to first search hit in this relation");
searchPanel.add(this.previousFast);
this.previous = new JButton(this.createIcon("res/REVERSE.gif"));
this.previous.addActionListener(pController);
this.previous.setActionCommand(UpdateViewerController.CMD_PREVIOUS);
this.previous.setToolTipText("Go to previous search hit");
searchPanel.add(this.previous);
this.next = new JButton(this.createIcon("res/play.gif"));
this.next.addActionListener(pController);
this.next.setActionCommand(UpdateViewerController.CMD_NEXT);
this.next.setToolTipText("Go to next search hit");
searchPanel.add(this.next);
this.nextFast = new JButton(this.createIcon("res/TOEND.gif"));
this.nextFast.addActionListener(pController);
this.nextFast.setActionCommand(UpdateViewerController.CMD_LAST);
this.nextFast.setToolTipText("Go to last search hit in this relation");
searchPanel.add(this.nextFast);
this.searchResultLabel = new JLabel();
searchPanel.add(this.searchResultLabel);
// replace panel
JPanel replacePanel = new JPanel();
replacePanel.setLayout(new FlowLayout());
this.replaceField = new JTextField(15);
this.replaceField.setEnabled(false);
replacePanel.add(this.replaceField);
this.replace = new JButton(UpdateViewerController.CMD_REPLACE);
this.replace.addActionListener(pController);
this.replace.setEnabled(false);
this.replace.setToolTipText("Replace current match and go to next");
replacePanel.add(this.replace);
this.replaceAll = new JButton(UpdateViewerController.CMD_REPLACE_ALL);
this.replaceAll.addActionListener(pController);
this.replaceAll.setEnabled(false);
this.replaceAll.setToolTipText("Replace in all loaded relations");
replacePanel.add(this.replaceAll);
this.print = new JButton(UpdateViewerController.CMD_PRINT);
this.print.addActionListener(pController);
replacePanel.add(this.print);
// layout
JPanel southPanel = new JPanel(new GridLayout(2, 1));
southPanel.add(searchPanel);
southPanel.add(replacePanel);
this.setLayout(new BorderLayout());
this.add(this.relScroll, BorderLayout.CENTER);
this.add(southPanel, BorderLayout.SOUTH);
this.clearSearch();
this.revalidate();
}
public boolean print(){
try{
return relTable.print();
} catch(Exception e){
return false;
}
}
/**
* Adds Change for specified row.
*/
public void addChange(int pRow, String pOldValue, String pNewValue)
{
RelationTableModel rtm = this.getTableModel();
int tupleIndex = Integer.valueOf((String)rtm.getValueAt(pRow, RelationTableModel.COL_TUPLEID));
int attributeIndex = rtm.rowToAttributeIndex(pRow);
String attributeName = this.relation.getAttributeNames().get(attributeIndex);
String attributeType = this.relation.getAttributeTypes().get(attributeIndex);
Change change = new Change(tupleIndex, attributeIndex, pRow,
attributeName, attributeType,
pOldValue, pNewValue);
rtm.addChange(change);
}
/*
* Adds a row set to the insert table, so that a (or another) tuple can be edited.
*/
public void addInsertTuple() throws InvalidRelationException
{
((RelationTableModel)this.insertTable.getModel()).addTuple(null);
this.validate();
this.repaint();
}
public boolean clearDeletions()
{
return this.getTableModel().clearDeletions();
}
/**
* Clears search results from search panel and table data.
*/
public void clearSearch()
{
this.search.setText(UpdateViewerController.CMD_SEARCH);
this.searchField.setText("");
this.replaceField.setText("");
this.searchField.setEnabled(true);
this.searchResultLabel.setText("");
this.next.setEnabled(false);
this.nextFast.setEnabled(false);
this.previous.setEnabled(false);
this.previousFast.setEnabled(false);
this.replaceField.setText("");
if (this.relTable != null)
{
this.getTableModel().setSearchHits(null);
//this.relTable.revalidate();
//this.relTable.repaint();
}
//this.repaint();
}
public boolean clearUpdateChanges()
{
return this.getTableModel().clearChanges();
}
/**
* Computes maximum content length of each table column
* and initializes maxContentLengths.
* TODO: nicer to have this done while scanning the relation during init phase.
*/
private int computeMaxContentLength(int pCol)
{
int max = 0;
FontMetrics metrics = ((Component)this).getFontMetrics(this.getFont());
if (pCol == 0 || pCol == 1)
{
List<String> strings = this.getTableModel().getColumnContent(pCol);
strings.add(this.getTableModel().getColumnName(pCol));
for (String s : strings)
{
int length = metrics.stringWidth(s);
if (length > max)
max = length;
}
}
// add some extra space
max = max + metrics.getMaxAdvance();
return max;
}
/*
* Creates and shows the table that displays the given relation in sequential manner.
* Returns true if paramater is valid list expression for relation.
*/
public boolean createFromLE(ListExpr pRelationLE, boolean pEditable)
{
try
{
SecondoObject relationSO = new SecondoObject(this.getName(), pRelationLE);
this.relation = new Relation();
this.relation.readFromSecondoObject(relationSO);
RelationTableModel rtm = new RelationTableModel(relation, pEditable);
this.relTable = new JTable(rtm);
this.relTable.setRowSelectionAllowed(false);
this.relTable.setColumnSelectionAllowed(false);
// suppress calling renderer every time the mouse is moved
ToolTipManager.sharedInstance().unregisterComponent(relTable);
// set column widths
TableColumn column = this.relTable.getColumnModel().getColumn(0);
column.setMinWidth(this.computeMaxContentLength(0));
column.setMaxWidth(this.computeMaxContentLength(0));
column = this.relTable.getColumnModel().getColumn(1);
column.setMinWidth(this.computeMaxContentLength(1));
column.setMaxWidth(this.computeMaxContentLength(1));
// set cell renderers
LabelTableCellRenderer lcr = new LabelTableCellRenderer();
column = this.relTable.getColumnModel().getColumn(0);
column.setCellRenderer(lcr);
column = this.relTable.getColumnModel().getColumn(1);
column.setCellRenderer(lcr);
column = this.relTable.getColumnModel().getColumn(2);
column.setCellRenderer(this.tableCellRenderer);
column.setCellEditor(this.tableCellEditor);
// set listeners
this.relTable.addPropertyChangeListener(this);
this.relTable.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent pEvent)
{
if (getState()==States.DELETE)
{
int row = relTable.rowAtPoint(pEvent.getPoint());
getTableModel().addDeletion(row);
}
}
});
this.relScroll.setViewportView(this.relTable);
this.relScroll.getVerticalScrollBar().setUnitIncrement(10);
this.relScroll.getVerticalScrollBar().setBlockIncrement(100);
this.repaint();
this.validate();
}
catch(InvalidRelationException e)
{
Reporter.debug(e);
Reporter.showError(e.getMessage());
return false;
}
return true;
}
/**
* Returns icon with image from given path, resized to specified width and height.
*/
public static ImageIcon createIcon(String pPath)
{
ImageIcon icon = new ImageIcon(ClassLoader.getSystemResource(pPath));
icon.setImage(icon.getImage().getScaledInstance(15, 15, Image.SCALE_DEFAULT));
return icon;
}
/**
* Deletes marked tuples from the table(model).
*/
public void deleteTuples()
{
RelationTableModel rtm = this.getTableModel();
if (rtm != null)
{
List<String> deleteIds = new ArrayList<String>(rtm.getDeletions());
for (String id : deleteIds)
{
rtm.removeTuple(id);
}
rtm.clearDeletions();
}
this.relTable.revalidate();
this.revalidate();
this.repaint();
}
/**
* Returns TRUE if checkbox case-sensitive is checked.
*/
public boolean getCaseSensitive()
{
return this.chkCaseSensitive.isSelected();
}
/**
* Returns index of current search hit, -1 if none exists.
*/
public int getCurrentHitIndex()
{
return this.getTableModel().getCurrentHitIndex();
}
/*
* Returns ids of all tuples that were marked to be deleted.
*/
public List<String> getDeleteTuples()
{
List<String> del = this.getTableModel().getDeletions();
Reporter.debug("RelationPanel.getDeleteTuples: " + del);
return this.getTableModel().getDeletions();
}
/**
* Returns index of current search hit, -1 if none exists.
*/
public SearchHit getHit(int pIndex)
{
return this.getTableModel().getHit(pIndex);
}
/**
* Returns number of current search hits, -1 if none exists.
*/
public int getHitCount()
{
return this.getTableModel().getHitCount();
}
/*
* Returns values to be inserted.
*/
public List<Tuple> getInsertTuples()
{
List<Tuple> tuples = new ArrayList<Tuple>();
for (int i = 0; i < this.insertRelation.getTupleCount(); i++)
{
tuples.add(this.insertRelation.getTupleAt(i));
}
//Reporter.debug("RelationPanel.getInsertTuples: no. of insertTuples is " + tuples.size());
return tuples;
}
/*
* Get relation name.
*/
public String getName() {
return this.name;
}
/*
* Returns the relation data.
*/
public Relation getRelation()
{
return this.relation;
}
/**
* Returns RelationTabelModel of currently displayed Table. (Convenience shortcut)
*/
private RelationTableModel getTableModel()
{
return (RelationTableModel)this.relTable.getModel();
}
/**
* Returns text from Replace field.
*/
public String getReplacement()
{
return this.replaceField.getText();
}
/**
* Returns text from Search field.
*/
public boolean getSearchActive()
{
return this.searchActive;
}
/**
* Returns text from Search field.
*/
public String getSearchKey()
{
return this.searchField.getText();
}
/**
* Returns state of RelationTabelModel.
*/
public int getState()
{
return this.getTableModel().getState();
}
/*
* Returns values to be updated.
*/
public Map<Integer, HashMap<String, Change>> getUpdateTuples()
{
return this.getTableModel().getChangesForUpdate();
}
/**
* Scrolls table so that specified position within specified row is displayed.
* Some distance to border is added for better context viewing.
* @param pRow row index
* @param pStartPosition position of first within cell
**/
public void goTo(int pRow, int pStartPosition, int pEndPosition)
{
this.relTable.changeSelection(pRow, RelationTableModel.COL_ATTRVALUE, false, false);
// get position offset within cell
Rectangle offset = null;
try
{
if (this.relTable.editCellAt(pRow, RelationTableModel.COL_ATTRVALUE, null))
{
ValueTableCellEditor tc = (ValueTableCellEditor)this.relTable.getCellEditor(pRow, RelationTableModel.COL_ATTRVALUE);
tc.setCaret(pStartPosition, pEndPosition);
offset = tc.getOffset(pStartPosition);
}
else
{
ValueTableCellRenderer tc = (ValueTableCellRenderer)this.relTable.getCellRenderer(pRow, RelationTableModel.COL_ATTRVALUE);
tc.setCaret(pStartPosition, pEndPosition);
offset = tc.getOffset(pStartPosition);
}
}
catch(BadLocationException e)
{
Reporter.debug(e);
Reporter.writeError(e.getMessage());
}
//JViewport viewport = (JViewport)relTable.getParent();
JViewport viewport = this.relScroll.getViewport();
Rectangle viewRect = viewport.getViewRect();
// get cell bounds
Rectangle rect = relTable.getCellRect(pRow, RelationTableModel.COL_ATTRVALUE, true);
if (offset != null)
{
rect.setLocation(rect.x + offset.x, rect.y + offset.y);
}
else
{
Reporter.debug("RelationPanel.goTo: position not found: " + pStartPosition);
}
rect.setLocation(rect.x - viewRect.x, rect.y - viewRect.y);
int centerX = (viewRect.width - rect.width) / 2;
int centerY = (viewRect.height - rect.height) / 2;
if (rect.x < centerX)
{
centerX = -centerX;
}
if (rect.y < centerY)
{
centerY = -centerY;
}
rect.translate(centerX, centerY);
viewport.scrollRectToVisible(rect);
}
/** Starts the editing the specified cell within the relation table **/
public void goTo(String pAttributeName, String pTupleId, int pOffset)
{
int row = this.getTableModel().getRow(pTupleId, pAttributeName);
try
{
this.goTo(row, pOffset, pOffset);
}
catch(Exception e)
{
Reporter.debug(e);
}
}
/** Starts the editing the specified cell within the insert table **/
public void goToInsert(int pRow)
{
try
{
this.insertTable.changeSelection(pRow, RelationTableModel.COL_ATTRVALUE, true, false);
this.insertTable.editCellAt(pRow, RelationTableModel.COL_ATTRVALUE, null);
}
catch(Exception e)
{
Reporter.debug(e);
}
}
/**
* Returns TRUE if last search has found matches in this relation table.
*/
public boolean hasSearchHits()
{
return this.getTableModel().hasSearchHits();
}
/**
* Inserts the specified tuple into Table(model)
*/
public void insertTuple(ListExpr pTuple)
{
this.getTableModel().addTuple(pTuple);
this.relTable.revalidate();
this.revalidate();
this.repaint();
}
/**
* Reacts on PropertyChangeEvent indicating that table cells were edited.
*/
public void processEditing()
{
if (this.relTable.isEditing())
{
// Editing started
// Save old value of currently active editable table cell.
this.oldEditCellValue = (String)this.tableCellEditor.getCellEditorValue();
//Reporter.debug("processEditingStarted: saved old value in cell ("
// + this.relTable.getSelectedRow() + ", " + this.relTable.getSelectedColumn() + ")" );
}
else
{
// Editing stopped
int row = this.relTable.getEditingRow();
String newValue = (String)this.tableCellEditor.getCellEditorValue();
if (!newValue.equals(this.oldEditCellValue))
{
RelationTableModel rtm = this.getTableModel();
// write changed cell value back into table model
rtm.setValueAt(newValue, row, RelationTableModel.COL_ATTRVALUE);
// create Change for update or undo actions
this.addChange(row, this.oldEditCellValue, newValue);
// update search hits for edited cell
this.updateSearchHits(row);
//Reporter.debug("RelationPanel.processEditingStopped: new value of table cell (" + row + ", " + col + ") is " + newValue) ;
}
}
}
/**
* Implemention of PropertyChangeListener interface.
*/
public void propertyChange(PropertyChangeEvent e)
{
// A table cell has started/stopped editing
if ("tableCellEditor".equals(e.getPropertyName()))
{
this.processEditing();
}
}
/*
* Removes the last added tuple from the insert relation.
* if it has more than one tuple.
*/
public boolean removeLastInsertTuple()
{
int tupleCount = insertRelation.getTupleCount();
if (tupleCount > 1)
{
this.insertRelation.removeTupleByIndex(tupleCount-1);
this.validate();
this.repaint();
return true;
}
return false;
}
/**
* Replaces the text at the given location by the replacement string and creates a change,
* so replacements can be undone.
* Replacement is only possible in Update mode.
*/
public void replace(SearchHit pHit)
{
//Reporter.debug("RelationPanel.replace: relation=" + this.getName() + ", hit=" + pHit.toString());
String replacement = this.getReplacement();
if (this.getState() == States.UPDATE && pHit != null && replacement != null)
{
int row = pHit.getRowIndex();
if (this.relTable.isEditing())
{
this.relTable.getCellEditor(row, RelationTableModel.COL_ATTRVALUE).stopCellEditing();
}
RelationTableModel rtm = this.getTableModel();
if (rtm != null)
{
String oldValue = (String)rtm.getValueAt(row, RelationTableModel.COL_ATTRVALUE);
String newValue = oldValue.substring(0, pHit.getStart())
+ replacement
+ oldValue.substring(pHit.getEnd());
//Reporter.debug("RelationPanel.replace: relation=" + this.getName() + ", row=" + row + ", oldvalue=" + oldValue + ", newValue=" + newValue);
// write changed cell value back into table model
rtm.setValueAt(newValue, row, RelationTableModel.COL_ATTRVALUE);
// create Change for update or undo actions
this.addChange(row, oldValue, newValue);
// update SearchHits for this cell
this.updateSearchHits(row);
}
}
}
/*
* Reset for Delete mode:
* Clears uncommitted deletions.
*/
public void resetDeleteSelections()
{
this.relTable.clearSelection();
this.getTableModel().clearDeletions();
}
/*
* Removes the table with insert tuples.
*/
public void resetInsert()
{
this.remove(this.splitPane);
this.add(relScroll, BorderLayout.CENTER);
this.validate();
}
/*
* Reset for Update mode:
* Sets table to original data and removes uncommitted changes.
*/
public void resetUpdateChanges()
{
//Reporter.debug("RelationPanel.resetUpdateChanges");
this.takeOverLastEditing(true);
RelationTableModel rtm = this.getTableModel();
List<Change> changes = rtm.getChanges();
// reset table to original data
for(int i=changes.size()-1; i>=0; i--)
{
Change ch = changes.get(i);
relTable.setValueAt(ch.getOldValue(), ch.getRowIndex(), 2);
rtm.removeChange(ch);
Reporter.debug("RelationPanel.resetUpdateChanges: reset and removed change " + ch.toString());
}
this.relTable.revalidate();
}
/**
* Returns SearchHits (containing row, start and end position within cell)
* of all occurences for specified search key.
* @param pKey search key (java regex pattern)
* @param pCaseSensitive
*/
public List<SearchHit> retrieveSearchHits(String pKey, boolean pCaseSensitive)
{
List<SearchHit> result = new ArrayList<SearchHit>();
if (pKey != null && (pKey.length() > 0 ))
{
for (int i = 0; i < this.getTableModel().getRowCount(); i++)
{
result.addAll(this.retrieveSearchHits(pKey, pCaseSensitive, i));
}
}
return result;
}
/**
* Returns hit positions for given key in a single table value cell.
*/
public List<SearchHit> retrieveSearchHits(String pKey, boolean pCaseSensitive, int pRow)
{
List<SearchHit> result = new ArrayList<SearchHit>();
String cellValue = (String)this.getTableModel().getValueAt(pRow, RelationTableModel.COL_ATTRVALUE);
Pattern pattern = pCaseSensitive ? Pattern.compile(pKey) : Pattern.compile(pKey, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(cellValue);
boolean found = matcher.find();
while (found)
{
SearchHit hit = new SearchHit(pRow, matcher.start(), matcher.start() + pKey.length());
result.add(hit);
found = matcher.find();
//Reporter.debug("RelationPanel.retrieveSearchHits: match in " + this.getName() + ": " + hit.toString());
}
return result;
}
/**
* Sets checkbox case-sensitive.
*/
public void setCaseSensitive(boolean pCaseSensitive)
{
this.chkCaseSensitive.setSelected(pCaseSensitive);
}
/**
* Sets replace field.
*/
public void setReplacement(String pReplacement)
{
this.replaceField.setText(pReplacement);
}
/**
* Sets search state.
* @param pSearchActive if TRUE then RelationPanel needs to redo search when cells were edited.
*/
public void setSearchActive(boolean pSearchActive)
{
this.searchActive = pSearchActive;
}
/**
*
*/
public void setSearchHits(List<SearchHit> pHits)
{
this.getTableModel().setSearchHits(pHits);
}
/**
* Sets search field.
*/
public void setSearchKey(String pSearchKey)
{
this.searchField.setText(pSearchKey);
}
/**
* Sets state and changes table properties accordingly.
* @param pSelectMode one of the States constants
* @see viewer.update2.States
*/
public void setState(int pState)
{
//Reporter.debug("RelationPanel.setState: oldState of relation " + this.getName() + " is " + this.getTableModel().getState());
if (this.getTableModel().getState() == States.LOADED_READ_ONLY)
{
if (pState==States.UPDATE || pState==States.DELETE || pState==States.INSERT)
{
this.relTable.setEnabled(false);
}
else
{
this.relTable.setEnabled(true);
}
}
else
{
this.getTableModel().setState(pState);
switch (pState)
{
case States.DELETE:
{
this.relTable.setRowSelectionAllowed(true);
this.relTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
break;
}
case States.INSERT:
{
((RelationTableModel)this.insertTable.getModel()).setState(pState);
break;
}
case States.UPDATE:
{
this.replaceField.setEnabled(true);
this.replace.setEnabled(true);
this.replaceAll.setEnabled(true);
break;
}
case States.FORMAT:
{
this.replaceField.setEnabled(false);
this.replace.setEnabled(false);
this.replaceAll.setEnabled(false);
break;
}
default:
{
this.relTable.setRowSelectionAllowed(false);
this.replaceField.setText("");
this.replaceField.setEnabled(false);
this.replace.setEnabled(false);
this.replaceAll.setEnabled(false);
}
}
}
//Reporter.debug("RelationPanel.setState: newState of relation " + this.getName() + " is " + this.getTableModel().getState());
}
/**
* Shows specified search hit (if valid)
* scrolls table, sets text and number of current hit in search panel.
*/
public boolean showHit(int pIndex)
{
Reporter.debug("RelationPanel.showHit: relation=" + this.getName() + ", hit=" + pIndex);
SearchHit hit = this.getTableModel().getHit(pIndex);
if (hit == null)
{
Reporter.debug("RelationPanel.showHit: hit=null!");
return false;
}
this.getTableModel().setCurrentHitIndex(pIndex);
this.goTo(hit.getRowIndex(), hit.getStart(), hit.getEnd());
this.searchResultLabel.setText((pIndex+1) + " / " + this.getTableModel().getHitCount());
return true;
}
/*
* Shows an empty relation that can be edited to contain tuples that shall be inserted.
*/
public void showInsertTable() throws InvalidRelationException
{
this.insertRelation = this.relation.createEmptyClone();
this.insertRelation.addTuple(this.insertRelation.createEmptyTuple());
RelationTableModel dtm = new RelationTableModel(this.insertRelation, true);
this.insertTable = new JTable(dtm);
// set column width and renderers
TableColumn column = this.insertTable.getColumnModel().getColumn(0);
column.setMinWidth(this.relTable.getColumnModel().getColumn(0).getMinWidth());
column.setMaxWidth(this.relTable.getColumnModel().getColumn(0).getMaxWidth());
column.setCellRenderer(new LabelTableCellRenderer());
column = this.insertTable.getColumnModel().getColumn(1);
column.setMinWidth(this.relTable.getColumnModel().getColumn(1).getMinWidth());
column.setMaxWidth(this.relTable.getColumnModel().getColumn(1).getMaxWidth());
column.setCellRenderer(new LabelTableCellRenderer());
column = this.insertTable.getColumnModel().getColumn(2);
column.setCellRenderer(this.tableCellRenderer);
column.setCellEditor(this.tableCellEditor);
// replace
this.insertScroll.setViewportView(insertTable);
this.splitPane.setTopComponent(this.relScroll);
this.splitPane.setBottomComponent(this.insertScroll);
this.splitPane.setResizeWeight(0.5);
this.remove(relScroll);
this.add(this.splitPane, BorderLayout.CENTER);
this.revalidate();
this.repaint();
}
/**
* Sets buttons and number of search results in search panel.
*/
public void showSearchResult()
{
this.search.setText(UpdateViewerController.CMD_CLEAR_SEARCH);
if (!this.getTableModel().hasSearchHits())
{
this.searchResultLabel.setText("0 / 0");
}
else
{
int curr = this.getTableModel().getCurrentHitIndex();
this.searchResultLabel.setText(curr+1 + " / " + this.getTableModel().getHitCount());
this.next.setEnabled(true);
this.nextFast.setEnabled(true);
this.previous.setEnabled(true);
this.previousFast.setEnabled(true);
}
this.revalidate();
this.repaint();
}
/*
The last cell the user edited before he pressed "commit" is usually not considered
because "tableChanged" will only be called, if he pressed "return" or selected a different cell
of the JTable before "commiting". Therefore this method takes over the value of the
last edited cell that was not taken into consideration yet.
*/
public void takeOverLastEditing(boolean updateMode)
{
if (updateMode)
{
if (relTable.isEditing())
{
int editedRow = relTable.getEditingRow();
int editedColumn = relTable.getEditingColumn();
CellEditor editor = relTable.getCellEditor(editedRow, editedColumn);
editor.stopCellEditing();
}
}
else
{
if (insertTable.isEditing())
{
int editedRow = this.insertTable.getEditingRow();
int editedColumn = this.insertTable.getEditingColumn();
CellEditor editor = this.insertTable.getCellEditor(editedRow, editedColumn);
editor.stopCellEditing();
}
}
}
/**
* Resets the last selection in Delete mode.
* Returns TRUE if after execution there are still deletions left to undo.
*/
public boolean undoLastDeleteSelection()
{
if (this.getTableModel().removeLastDeletion())
{
this.validate();
this.repaint();
}
return this.getTableModel().hasDeletions();
}
/**
* Resets the last editited cell in updatemode to its original value.
* Returns TRUE if after execution there are still changes left to undo.
*/
public boolean undoLastUpdateChange()
{
boolean result = false;
this.takeOverLastEditing(true);
Change ch = this.getTableModel().getLastChange();
if (ch != null)
{
relTable.setValueAt(ch.getOldValue(), ch.getRowIndex(), RelationTableModel.COL_ATTRVALUE);
this.goTo(ch.getRowIndex(), 0, 0);
this.getTableModel().removeChange(ch);
if (this.getTableModel().getLastChange() != null)
{
result = true;
}
this.validate();
this.repaint();
}
return result;
}
/**
* Updates the SearchHits for specified row.
*/
public void updateSearchHits(int pRow)
{
// update search hits for edited cell
if (this.searchActive)
{
Reporter.debug("RelationPanel.updateSearchHits") ;
RelationTableModel rtm = this.getTableModel();
List<SearchHit> hits = rtm.getHits(pRow);
if (hits != null && !hits.isEmpty())
{
for (SearchHit h : hits)
{
rtm.removeHit(h);
}
}
for (SearchHit newHit : this.retrieveSearchHits(this.getSearchKey(), this.getCaseSensitive(), pRow))
{
rtm.addHit(newHit);
}
this.showSearchResult();
}
}
}