package net.sf.jabref;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;
class TableColumnsTab extends JPanel implements PrefsTab {
JabRefPreferences _prefs;
private boolean tableChanged = false;
private JTable colSetup;
private int rowCount = -1, ncWidth = -1;
private Vector<TableRow> tableRows = new Vector<TableRow>(10);
private JabRefFrame frame;
class TableRow {
String name;
int length;
public TableRow(String name) {
this.name = name;
length = GUIGlobals.DEFAULT_FIELD_LENGTH;
}
public TableRow(int length) {
this.length = length;
name = "";
}
public TableRow(String name, int length) {
this.name = name;
this.length = length;
}
}
/**
* Customization of external program paths.
*
* @param prefs a <code>JabRefPreferences</code> value
*/
public TableColumnsTab(JabRefPreferences prefs, JabRefFrame frame) {
_prefs = prefs;
this.frame = frame;
setLayout(new BorderLayout());
TableModel tm = new AbstractTableModel() {
public int getRowCount() { return rowCount; }
public int getColumnCount() { return 2; }
public Object getValueAt(int row, int column) {
if (row == 0)
return (column==0 ? GUIGlobals.NUMBER_COL : ""+ncWidth);
row--;
if (row >= tableRows.size())
return "";
Object rowContent = tableRows.elementAt(row);
if (rowContent == null)
return "";
TableRow tr = (TableRow)rowContent;
switch (column) {
case 0:
return tr.name;
case 1:
return ((tr.length > 0) ? Integer.toString(tr.length) : "");
}
return null; // Unreachable.
}
public String getColumnName(int col) {
return (col == 0 ? Globals.lang("Field name") : Globals.lang("Column width"));
}
public Class<?> getColumnClass(int column) {
if (column == 0)
return String.class;
else
return Integer.class;
}
public boolean isCellEditable(int row, int col) {
return !((row == 0) && (col == 0));
}
public void setValueAt(Object value, int row, int col) {
tableChanged = true;
// Make sure the vector is long enough.
while (row >= tableRows.size())
tableRows.add(new TableRow("", -1));
if ((row == 0) && (col == 1)) {
ncWidth = Integer.parseInt(value.toString());
return;
}
TableRow rowContent = tableRows.elementAt(row-1);
if (col == 0) {
rowContent.name = value.toString();
if (((String)getValueAt(row, 1)).equals(""))
setValueAt(""+GUIGlobals.DEFAULT_FIELD_LENGTH, row, 1);
}
else {
if (value == null) rowContent.length = -1;
else rowContent.length = Integer.parseInt(value.toString());
}
}
};
colSetup = new JTable(tm);
TableColumnModel cm = colSetup.getColumnModel();
cm.getColumn(0).setPreferredWidth(140);
cm.getColumn(1).setPreferredWidth(80);
FormLayout layout = new FormLayout
("1dlu, 8dlu, left:pref, 4dlu, fill:pref",//, 4dlu, fill:60dlu, 4dlu, fill:pref",
"");
DefaultFormBuilder builder = new DefaultFormBuilder(layout);
JPanel pan = new JPanel();
JPanel tabPanel = new JPanel();
tabPanel.setLayout(new BorderLayout());
JScrollPane sp = new JScrollPane
(colSetup, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
colSetup.setPreferredScrollableViewportSize(new Dimension(250,200));
sp.setMinimumSize(new Dimension(250,300));
tabPanel.add(sp, BorderLayout.CENTER);
JToolBar tlb = new JToolBar(SwingConstants.VERTICAL);
tlb.setFloatable(false);
//tlb.setRollover(true);
//tlb.setLayout(gbl);
AddRowAction ara = new AddRowAction();
DeleteRowAction dra = new DeleteRowAction();
MoveRowUpAction moveUp = new MoveRowUpAction();
MoveRowDownAction moveDown = new MoveRowDownAction();
tlb.setBorder(null);
tlb.add(ara);
tlb.add(dra);
tlb.addSeparator();
tlb.add(moveUp);
tlb.add(moveDown);
//tlb.addSeparator();
//tlb.add(new UpdateWidthsAction());
tabPanel.add(tlb, BorderLayout.EAST);
builder.appendSeparator(Globals.lang("Entry table columns"));
builder.nextLine();
builder.append(pan); builder.append(tabPanel); builder.nextLine();
// lab = new JLabel("<HTML>("+Globals.lang("this button will update the column width settings<BR>"
// +"to match the current widths in your table")+")</HTML>");
// lab = new JLabel("<HTML>("+Globals.lang("this_button_will_update") +")</HTML>") ;
builder.append(pan);
JButton buttonWidth = new JButton(new UpdateWidthsAction());
JButton buttonOrder = new JButton(new UpdateOrderAction());
builder.append(buttonWidth);builder.nextLine();
builder.append(pan);
builder.append(buttonOrder);builder.nextLine();
builder.append(pan);
//builder.append(lab);
builder.nextLine();
pan = builder.getPanel();
pan.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
add(pan, BorderLayout.CENTER);
}
public void setValues() {
tableRows.clear();
String[] names = _prefs.getStringArray("columnNames"),
lengths = _prefs.getStringArray("columnWidths");
for (int i=0; i<names.length; i++) {
if (i<lengths.length)
tableRows.add(new TableRow(names[i], Integer.parseInt(lengths[i])));
else
tableRows.add(new TableRow(names[i]));
}
rowCount = tableRows.size()+5;
ncWidth = _prefs.getInt("numberColWidth");
}
class DeleteRowAction extends AbstractAction {
public DeleteRowAction() {
super("Delete row", GUIGlobals.getImage("remove"));
putValue(SHORT_DESCRIPTION, Globals.lang("Delete rows"));
}
public void actionPerformed(ActionEvent e) {
int[] rows = colSetup.getSelectedRows();
if (rows.length == 0)
return;
int offs = 0;
for (int i=rows.length-1; i>=0; i--) {
if ((rows[i] <= tableRows.size()) && (rows[i] != 0)) {
tableRows.remove(rows[i]-1);
offs++;
}
}
rowCount -= offs;
if (rows.length > 1) colSetup.clearSelection();
colSetup.revalidate();
colSetup.repaint();
tableChanged = true;
}
}
class AddRowAction extends AbstractAction {
public AddRowAction() {
super("Add row", GUIGlobals.getImage("add"));
putValue(SHORT_DESCRIPTION, Globals.lang("Insert rows"));
}
public void actionPerformed(ActionEvent e) {
int[] rows = colSetup.getSelectedRows();
if (rows.length == 0) {
// No rows selected, so we just add one at the end.
rowCount++;
colSetup.revalidate();
colSetup.repaint();
return;
}
for (int i=0; i<rows.length; i++) {
if (rows[i]+i-1 < tableRows.size())
tableRows.add(Math.max(0, rows[i]+i-1), new TableRow(GUIGlobals.DEFAULT_FIELD_LENGTH));
}
rowCount += rows.length;
if (rows.length > 1) colSetup.clearSelection();
colSetup.revalidate();
colSetup.repaint();
tableChanged = true;
}
}
abstract class AbstractMoveRowAction extends AbstractAction {
public AbstractMoveRowAction(String string, ImageIcon image) {
super(string, image);
}
protected void swap(int i, int j) {
if (i < 0 || i >= tableRows.size())
return;
if (j < 0 || j >= tableRows.size())
return;
TableRow tmp = tableRows.get(i);
tableRows.set(i, tableRows.get(j));
tableRows.set(j, tmp);
}
}
class MoveRowUpAction extends AbstractMoveRowAction {
public MoveRowUpAction() {
super("Up", GUIGlobals.getImage("up"));
putValue(SHORT_DESCRIPTION, Globals.lang("Move up"));
}
public void actionPerformed(ActionEvent e) {
int selected[] = colSetup.getSelectedRows();
Arrays.sort(selected);
// first element (#) not inside tableRows
// don't move if a selected element is at bounce
if (selected.length > 0 && selected[0] > 1) {
boolean newSelected[] = new boolean[colSetup.getRowCount()];
for (int i : selected) {
swap(i - 1, i - 2);
newSelected[i - 1] = true;
}
// select all and remove unselected
colSetup.setRowSelectionInterval(0, colSetup.getRowCount() - 1);
for (int i = 0; i < colSetup.getRowCount(); i++) {
if (!newSelected[i])
colSetup.removeRowSelectionInterval(i, i);
}
colSetup.revalidate();
colSetup.repaint();
tableChanged = true;
}
}
}
class MoveRowDownAction extends AbstractMoveRowAction {
public MoveRowDownAction() {
super("Down", GUIGlobals.getImage("down"));
putValue(SHORT_DESCRIPTION, Globals.lang("Down"));
}
public void actionPerformed(ActionEvent e) {
int selected[] = colSetup.getSelectedRows();
Arrays.sort(selected);
final int last = selected.length - 1;
boolean newSelected[] = new boolean[colSetup.getRowCount()];
// don't move if a selected element is at bounce
if (selected.length > 0 && selected[last] < tableRows.size()) {
for (int i = last; i >= 0; i--) {
swap(selected[i] - 1, selected[i]);
newSelected[selected[i] + 1] = true;
}
// select all and remove unselected
colSetup.setRowSelectionInterval(0, colSetup.getRowCount() - 1);
for (int i = 0; i < colSetup.getRowCount(); i++) {
if (!newSelected[i])
colSetup.removeRowSelectionInterval(i, i);
}
colSetup.revalidate();
colSetup.repaint();
tableChanged = true;
}
}
}
class UpdateOrderAction extends AbstractAction {
public UpdateOrderAction() {
super(Globals.lang("Update to current column order"));
}
public void actionPerformed(ActionEvent e) {
BasePanel panel = frame.basePanel();
if (panel == null) {
return;
}
// idea: sort elements according to value stored in hash, keep
// everything not inside hash/mainTable as it was
final HashMap<String, Integer> map = new HashMap<String, Integer>();
// first element (#) not inside tableRows
for (int i = 1; i < panel.mainTable.getColumnCount(); i++) {
String name = panel.mainTable.getColumnName(i);
if (name != null && name.length() != 0) {
map.put(name.toLowerCase(), i);
}
}
Collections.sort(tableRows, new Comparator<TableRow>() {
public int compare(TableRow o1, TableRow o2) {
Integer n1 = map.get(o1.name);
Integer n2 = map.get(o2.name);
if (n1 == null || n2 == null) {
return 0;
}
return n1.compareTo(n2);
}
});
colSetup.revalidate();
colSetup.repaint();
tableChanged = true;
}
}
class UpdateWidthsAction extends AbstractAction {
public UpdateWidthsAction() {
//super(Globals.lang("Update to current column widths"));
super(Globals.lang("Update to current column widths"));
//putValue(SHORT_DESCRIPTION, Globals.lang("Update to current column widths"));
}
public void actionPerformed(ActionEvent e) {
BasePanel panel = frame.basePanel();
if (panel == null) return;
TableColumnModel colMod = panel.mainTable.getColumnModel();
colSetup.setValueAt(""+colMod.getColumn(0).getWidth(), 0, 1);
for (int i=1; i<colMod.getColumnCount(); i++) {
try {
String name = panel.mainTable.getColumnName(i).toLowerCase();
int width = colMod.getColumn(i).getWidth();
//Util.pr(":"+((String)colSetup.getValueAt(i-1, 0)).toLowerCase());
//Util.pr("-"+name);
if ((i <= tableRows.size()) && (((String)colSetup.getValueAt(i, 0)).toLowerCase()).equals(name))
colSetup.setValueAt(""+width, i, 1);
else { // Doesn't match; search for a matching col in our table
for (int j=0; j<colSetup.getRowCount(); j++) {
if ((j < tableRows.size()) &&
(((String)colSetup.getValueAt(j, 0)).toLowerCase()).equals(name)) {
colSetup.setValueAt(""+width, j, 1);
break;
}
}
}
} catch (Throwable ex) {
ex.printStackTrace();
}
colSetup.revalidate();
colSetup.repaint();
}
}
}
/**
* Store changes to table preferences. This method is called when
* the user clicks Ok.
*
*/
public void storeSettings() {
if (colSetup.isEditing()) {
int col = colSetup.getEditingColumn(),
row = colSetup.getEditingRow();
colSetup.getCellEditor(row, col).stopCellEditing();
}
//_prefs.putStringArray("columnNames", getChoices());
/*String[] cols = tableFields.getText().replaceAll("\\s+","")
.replaceAll("\\n+","").toLowerCase().split(";");
if (cols.length > 0) for (int i=0; i<cols.length; i++)
cols[i] = cols[i].trim();
else cols = null;*/
// Now we need to make sense of the contents the user has made to the
// table setup table.
if (tableChanged) {
// First we remove all rows with empty names.
int i=0;
while (i < tableRows.size()) {
if (tableRows.elementAt(i).name.equals(""))
tableRows.removeElementAt(i);
else i++;
}
// Then we make arrays
String[] names = new String[tableRows.size()],
widths = new String[tableRows.size()];
int[] nWidths = new int[tableRows.size()];
_prefs.putInt("numberColWidth", ncWidth);
for (i=0; i<tableRows.size(); i++) {
TableRow tr = tableRows.elementAt(i);
names[i] = tr.name;
nWidths[i] = tr.length;
widths[i] = ""+tr.length;
//Util.pr(names[i]+" "+widths[i]);
}
// Finally, we store the new preferences.
_prefs.putStringArray("columnNames", names);
_prefs.putStringArray("columnWidths", widths);
}
}
public boolean readyToClose() {
return true;
}
public String getTabName() {
return Globals.lang("Entry table columns");
}
}