package org.jabref.gui.maintable;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.TransferHandler;
import javax.swing.plaf.TableUI;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import org.jabref.Globals;
import org.jabref.gui.BasePanel;
import org.jabref.gui.EntryMarker;
import org.jabref.gui.GUIGlobals;
import org.jabref.gui.JabRefFrame;
import org.jabref.gui.groups.EntryTableTransferHandler;
import org.jabref.gui.groups.GroupMatcher;
import org.jabref.gui.keyboard.KeyBinding;
import org.jabref.gui.renderer.CompleteRenderer;
import org.jabref.gui.renderer.GeneralRenderer;
import org.jabref.gui.renderer.IncompleteRenderer;
import org.jabref.gui.search.matchers.SearchMatcher;
import org.jabref.gui.util.comparator.FirstColumnComparator;
import org.jabref.gui.util.comparator.IconComparator;
import org.jabref.gui.util.comparator.RankingFieldComparator;
import org.jabref.logic.TypedBibEntry;
import org.jabref.logic.bibtex.comparator.FieldComparator;
import org.jabref.model.EntryTypes;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibtexSingleField;
import org.jabref.model.entry.EntryType;
import org.jabref.model.entry.FieldName;
import org.jabref.model.entry.specialfields.SpecialField;
import org.jabref.preferences.JabRefPreferences;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.gui.AbstractTableComparatorChooser;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.swing.DefaultEventSelectionModel;
import ca.odell.glazedlists.swing.GlazedListsSwing;
import ca.odell.glazedlists.swing.TableComparatorChooser;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class MainTable extends JTable {
private static GeneralRenderer defRenderer;
private static GeneralRenderer reqRenderer;
private static GeneralRenderer optRenderer;
private static GeneralRenderer resolvedRenderer;
private static GeneralRenderer grayedOutRenderer;
private static GeneralRenderer veryGrayedOutRenderer;
private static List<GeneralRenderer> markedRenderers;
private static IncompleteRenderer incRenderer;
private static CompleteRenderer compRenderer;
private static CompleteRenderer grayedOutNumberRenderer;
private static CompleteRenderer veryGrayedOutNumberRenderer;
private static List<CompleteRenderer> markedNumberRenderers;
private static final Log LOGGER = LogFactory.getLog(MainTable.class);
private final MainTableFormat tableFormat;
private final BasePanel panel;
private final boolean tableColorCodes;
private final boolean tableResolvedColorCodes;
private final DefaultEventSelectionModel<BibEntry> localSelectionModel;
private final TableComparatorChooser<BibEntry> comparatorChooser;
private final JScrollPane pane;
// needed to activate/deactivate the listener
private final PersistenceTableColumnListener tableColumnListener;
private final MainTableDataModel model;
// Enum used to define how a cell should be rendered.
private enum CellRendererMode {
REQUIRED,
RESOLVED,
OPTIONAL,
OTHER
}
static {
MainTable.updateRenderers();
}
public MainTable(MainTableFormat tableFormat, MainTableDataModel model, JabRefFrame frame,
BasePanel panel) {
super();
this.model = model;
addFocusListener(Globals.getFocusListener());
setAutoResizeMode(Globals.prefs.getInt(JabRefPreferences.AUTO_RESIZE_MODE));
this.tableFormat = tableFormat;
this.panel = panel;
setModel(GlazedListsSwing
.eventTableModelWithThreadProxyList(model.getTableRows(), tableFormat));
tableColorCodes = Globals.prefs.getBoolean(JabRefPreferences.TABLE_COLOR_CODES_ON);
tableResolvedColorCodes = Globals.prefs.getBoolean(JabRefPreferences.TABLE_RESOLVED_COLOR_CODES_ON);
localSelectionModel = (DefaultEventSelectionModel<BibEntry>) GlazedListsSwing
.eventSelectionModelWithThreadProxyList(model.getTableRows());
setSelectionModel(localSelectionModel);
pane = new JScrollPane(this);
pane.setBorder(BorderFactory.createEmptyBorder());
pane.getViewport().setBackground(Globals.prefs.getColor(JabRefPreferences.TABLE_BACKGROUND));
setGridColor(Globals.prefs.getColor(JabRefPreferences.GRID_COLOR));
if (Globals.prefs.getBoolean(JabRefPreferences.TABLE_SHOW_GRID)) {
setShowGrid(true);
} else {
setShowGrid(false);
setIntercellSpacing(new Dimension(0, 0));
}
this.setTableHeader(new PreventDraggingJTableHeader(this, tableFormat));
comparatorChooser = this.createTableComparatorChooser(this, model.getSortedForUserDefinedTableColumnSorting(),
AbstractTableComparatorChooser.MULTIPLE_COLUMN_KEYBOARD);
this.tableColumnListener = new PersistenceTableColumnListener(this);
// set table header render AFTER creation of comparatorChooser (this enables sort arrow rendering)
this.getTableHeader().setDefaultRenderer(new MainTableHeaderRenderer(this.getTableHeader().getDefaultRenderer()));
// TODO: Figure out, whether this call is needed.
getSelected();
// enable DnD
setDragEnabled(true);
TransferHandler xfer = new EntryTableTransferHandler(this, frame, panel);
setTransferHandler(xfer);
pane.setTransferHandler(xfer);
setupComparatorChooser();
model.updateMarkingState(Globals.prefs.getBoolean(JabRefPreferences.FLOAT_MARKED_ENTRIES));
setWidths();
//Override 'selectNextColumnCell' and 'selectPreviousColumnCell' to move rows instead of cells on TAB
ActionMap actionMap = getActionMap();
InputMap inputMap = getInputMap();
actionMap.put("selectNextColumnCell", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
panel.selectNextEntry();
}
});
actionMap.put("selectPreviousColumnCell", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
panel.selectPreviousEntry();
}
});
actionMap.put("selectNextRow", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
panel.selectNextEntry();
}
});
actionMap.put("selectPreviousRow", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
panel.selectPreviousEntry();
}
});
String selectFirst = "selectFirst";
inputMap.put(Globals.getKeyPrefs().getKey(KeyBinding.SELECT_FIRST_ENTRY), selectFirst);
actionMap.put(selectFirst, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent event) {
panel.selectFirstEntry();
}
});
String selectLast = "selectLast";
inputMap.put(Globals.getKeyPrefs().getKey(KeyBinding.SELECT_LAST_ENTRY), selectLast);
actionMap.put(selectLast, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent event) {
panel.selectLastEntry();
}
});
}
public void addSelectionListener(ListEventListener<BibEntry> listener) {
getSelected().addListEventListener(listener);
}
public JScrollPane getPane() {
return pane;
}
public MainTableDataModel getTableModel() {
return model;
}
@Override
public String getToolTipText(MouseEvent e) {
String toolTipText = super.getToolTipText(e);
Point p = e.getPoint();
int col = columnAtPoint(p);
int row = rowAtPoint(p);
Rectangle bounds = getCellRect(row, col, false);
Dimension d = prepareRenderer(getCellRenderer(row, col), row, col).getPreferredSize();
// if the content of the cell is bigger than the cell itself render it as the tooltip (thus throwing the original tooltip away)
if ((d != null) && (d.width > bounds.width) && (getValueAt(row, col) != null)) {
toolTipText = getValueAt(row, col).toString();
}
return toolTipText;
}
@Override
public TableCellRenderer getCellRenderer(int row, int column) {
int score = -3;
DefaultTableCellRenderer renderer = MainTable.defRenderer;
if ((model.getSearchState() != MainTableDataModel.DisplayOption.FLOAT)
|| matches(row, SearchMatcher.INSTANCE)) {
score++;
}
if ((model.getGroupingState() != MainTableDataModel.DisplayOption.FLOAT)
|| matches(row, GroupMatcher.INSTANCE)) {
score += 2;
}
// Now, a grayed out renderer is for entries with -1, and
// a very grayed out one for entries with -2
if (score < -1) {
if (column == 0) {
MainTable.veryGrayedOutNumberRenderer.setNumber(row);
renderer = MainTable.veryGrayedOutNumberRenderer;
} else {
renderer = MainTable.veryGrayedOutRenderer;
}
}
else if (score == -1) {
if (column == 0) {
MainTable.grayedOutNumberRenderer.setNumber(row);
renderer = MainTable.grayedOutNumberRenderer;
} else {
renderer = MainTable.grayedOutRenderer;
}
}
else if (column == 0) {
if (isComplete(row)) {
MainTable.compRenderer.setNumber(row);
int marking = isMarked(row);
if (marking > 0) {
marking = Math.min(marking, EntryMarker.MARK_COLOR_LEVELS);
renderer = MainTable.markedNumberRenderers.get(marking - 1);
MainTable.markedNumberRenderers.get(marking - 1).setNumber(row);
} else {
renderer = MainTable.compRenderer;
}
} else {
// Return a renderer with red background if the entry is incomplete.
MainTable.incRenderer.setNumber(row);
renderer = MainTable.incRenderer;
}
} else if (tableColorCodes || tableResolvedColorCodes) {
CellRendererMode status = getCellStatus(row, column, tableResolvedColorCodes);
if (status == CellRendererMode.REQUIRED) {
renderer = MainTable.reqRenderer;
} else if (status == CellRendererMode.OPTIONAL) {
renderer = MainTable.optRenderer;
} else if (status == CellRendererMode.RESOLVED) {
renderer = MainTable.resolvedRenderer;
}
}
// For MARKED feature:
int marking = isMarked(row);
if ((column != 0) && (marking > 0)) {
marking = Math.min(marking, EntryMarker.MARK_COLOR_LEVELS);
renderer = MainTable.markedRenderers.get(marking - 1);
}
return renderer;
}
private void setWidths() {
// Setting column widths:
int ncWidth = Globals.prefs.getInt(JabRefPreferences.NUMBER_COL_WIDTH);
List<String> widthsFromPreferences = Globals.prefs.getStringList(JabRefPreferences.COLUMN_WIDTHS);
TableColumnModel cm = getColumnModel();
cm.getColumn(0).setPreferredWidth(ncWidth);
for (int i = 1; i < cm.getColumnCount(); i++) {
MainTableColumn mainTableColumn = tableFormat.getTableColumn(cm.getColumn(i).getModelIndex());
if (SpecialField.RANKING.getFieldName().equals(mainTableColumn.getColumnName())) {
cm.getColumn(i).setPreferredWidth(GUIGlobals.WIDTH_ICON_COL_RANKING);
cm.getColumn(i).setMinWidth(GUIGlobals.WIDTH_ICON_COL_RANKING);
cm.getColumn(i).setMaxWidth(GUIGlobals.WIDTH_ICON_COL_RANKING);
} else if (mainTableColumn.isIconColumn()) {
cm.getColumn(i).setPreferredWidth(GUIGlobals.WIDTH_ICON_COL);
cm.getColumn(i).setMinWidth(GUIGlobals.WIDTH_ICON_COL);
cm.getColumn(i).setMaxWidth(GUIGlobals.WIDTH_ICON_COL);
} else {
List<String> allColumns = Globals.prefs.getStringList(JabRefPreferences.COLUMN_NAMES);
// find index of current mainTableColumn in allColumns
for (int j = 0; j < allColumns.size(); j++) {
if (allColumns.get(j).equalsIgnoreCase(mainTableColumn.getDisplayName())) {
try {
// set preferred width by using found index j in the width array
cm.getColumn(i).setPreferredWidth(Integer.parseInt(widthsFromPreferences.get(j)));
} catch (NumberFormatException e) {
LOGGER.info("Exception while setting column widths. Choosing default.", e);
cm.getColumn(i).setPreferredWidth(BibtexSingleField.DEFAULT_FIELD_LENGTH);
}
break;
}
}
}
}
}
public BibEntry getEntryAt(int row) {
return model.getTableRows().get(row);
}
/**
* @return the return value is never null
*/
public List<BibEntry> getSelectedEntries() {
return new ArrayList<>(getSelected());
}
private List<Boolean> getCurrentSortOrder() {
List<Boolean> order = new ArrayList<>();
List<Integer> sortCols = comparatorChooser.getSortingColumns();
for (Integer i : sortCols) {
order.add(comparatorChooser.isColumnReverse(i));
}
return order;
}
private List<String> getCurrentSortFields() {
List<Integer> sortCols = comparatorChooser.getSortingColumns();
List<String> fields = new ArrayList<>();
for (Integer i : sortCols) {
// TODO check whether this really works
String name = tableFormat.getColumnName(i);
//TODO OLD
// String name = tableFormat.getColumnType(i);
if (name != null) {
fields.add(name.toLowerCase(Locale.ROOT));
}
}
return fields;
}
/**
* This method sets up what Comparators are used for the various table columns.
* The ComparatorChooser enables and disables such Comparators as the user clicks
* columns, but this is where the Comparators are defined. Also, the ComparatorChooser
* is initialized with the sort order defined in Preferences.
*/
private void setupComparatorChooser() {
// First column:
List<Comparator> comparators = comparatorChooser.getComparatorsForColumn(0);
comparators.clear();
comparators.add(new FirstColumnComparator(panel.getBibDatabaseContext()));
for (int i = 1; i < tableFormat.getColumnCount(); i++) {
MainTableColumn tableColumn = tableFormat.getTableColumn(i);
comparators = comparatorChooser.getComparatorsForColumn(i);
comparators.clear();
if (SpecialField.RANKING.getFieldName().equals(tableColumn.getColumnName())) {
comparators.add(new RankingFieldComparator());
} else if (tableColumn.isIconColumn()) {
comparators.add(new IconComparator(tableColumn.getBibtexFields()));
} else {
comparators = comparatorChooser.getComparatorsForColumn(i);
comparators.clear();
comparators.add(new FieldComparator(tableFormat.getColumnName(i).toLowerCase(Locale.ROOT)));
}
}
// Set initial sort columns:
// Default sort order:
String[] sortFields = new String[] {
Globals.prefs.get(JabRefPreferences.TABLE_PRIMARY_SORT_FIELD),
Globals.prefs.get(JabRefPreferences.TABLE_SECONDARY_SORT_FIELD),
Globals.prefs.get(JabRefPreferences.TABLE_TERTIARY_SORT_FIELD)
};
boolean[] sortDirections = new boolean[] {
Globals.prefs.getBoolean(JabRefPreferences.TABLE_PRIMARY_SORT_DESCENDING),
Globals.prefs.getBoolean(JabRefPreferences.TABLE_SECONDARY_SORT_DESCENDING),
Globals.prefs.getBoolean(JabRefPreferences.TABLE_TERTIARY_SORT_DESCENDING)
}; // descending
model.getSortedForUserDefinedTableColumnSorting().getReadWriteLock().writeLock().lock();
try {
for (int i = 0; i < sortFields.length; i++) {
int index = -1;
// TODO where is this prefix set?
// if (!sortFields[i].startsWith(MainTableFormat.ICON_COLUMN_PREFIX))
if (sortFields[i].startsWith("iconcol:")) {
for (int j = 0; j < tableFormat.getColumnCount(); j++) {
if (sortFields[i].equals(tableFormat.getColumnName(j))) {
index = j;
break;
}
}
} else {
index = tableFormat.getColumnIndex(sortFields[i]);
}
if (index >= 0) {
comparatorChooser.appendComparator(index, 0, sortDirections[i]);
}
}
} finally {
model.getSortedForUserDefinedTableColumnSorting().getReadWriteLock().writeLock().unlock();
}
// Add action listener so we can remember the sort order:
comparatorChooser.addSortActionListener(e -> {
// Get the information about the current sort order:
List<String> fields = getCurrentSortFields();
List<Boolean> order = getCurrentSortOrder();
// Update preferences:
int count = Math.min(fields.size(), order.size());
if (count >= 1) {
Globals.prefs.put(JabRefPreferences.TABLE_PRIMARY_SORT_FIELD, fields.get(0));
Globals.prefs.putBoolean(JabRefPreferences.TABLE_PRIMARY_SORT_DESCENDING, order.get(0));
}
if (count >= 2) {
Globals.prefs.put(JabRefPreferences.TABLE_SECONDARY_SORT_FIELD, fields.get(1));
Globals.prefs.putBoolean(JabRefPreferences.TABLE_SECONDARY_SORT_DESCENDING, order.get(1));
} else {
Globals.prefs.put(JabRefPreferences.TABLE_SECONDARY_SORT_FIELD, "");
Globals.prefs.putBoolean(JabRefPreferences.TABLE_SECONDARY_SORT_DESCENDING, false);
}
if (count >= 3) {
Globals.prefs.put(JabRefPreferences.TABLE_TERTIARY_SORT_FIELD, fields.get(2));
Globals.prefs.putBoolean(JabRefPreferences.TABLE_TERTIARY_SORT_DESCENDING, order.get(2));
} else {
Globals.prefs.put(JabRefPreferences.TABLE_TERTIARY_SORT_FIELD, "");
Globals.prefs.putBoolean(JabRefPreferences.TABLE_TERTIARY_SORT_DESCENDING, false);
}
});
}
private CellRendererMode getCellStatus(int row, int col, boolean checkResolved) {
try {
BibEntry be = getEntryAt(row);
if (checkResolved && tableFormat.getTableColumn(col).isResolved(be)) {
return CellRendererMode.RESOLVED;
}
Optional<EntryType> type = EntryTypes.getType(be.getType(), panel.getBibDatabaseContext().getMode());
if (type.isPresent()) {
String columnName = getColumnName(col).toLowerCase(Locale.ROOT);
if (columnName.equals(BibEntry.KEY_FIELD) || type.get().getRequiredFieldsFlat().contains(columnName)) {
return CellRendererMode.REQUIRED;
}
if (type.get().getOptionalFields().contains(columnName)) {
return CellRendererMode.OPTIONAL;
}
}
return CellRendererMode.OTHER;
} catch (NullPointerException ex) {
return CellRendererMode.OTHER;
}
}
/**
* Use with caution! If you modify an entry in the table, the selection changes
*
* You can avoid it with
* <code>.getSelected().getReadWriteLock().writeLock().lock()</code>
* and then <code>.unlock()</code>
*/
public EventList<BibEntry> getSelected() {
return localSelectionModel.getSelected();
}
/**
* Selects the given row
*
* @param row the row to select
*/
public void setSelected(int row) {
localSelectionModel.setSelectionInterval(row, row);
}
public int findEntry(BibEntry entry) {
EventList<BibEntry> tableRows = model.getTableRows();
for (int row = 0; row < tableRows.size(); row++) {
BibEntry bibEntry = tableRows.get(row);
if (entry == bibEntry) { // NOPMD (equals doesn't recognise duplicates)
return row;
}
}
return -1;
}
/**
* Method to check whether a MainTableColumn at the modelIndex refers to the file field (either as a specific
* file extension filter or not)
*
* @param modelIndex model index of the column to check
* @return true if the column shows the "file" field; false otherwise
*/
public boolean isFileColumn(int modelIndex) {
return (tableFormat.getTableColumn(modelIndex) != null) && tableFormat.getTableColumn(modelIndex)
.getBibtexFields().contains(FieldName.FILE);
}
private boolean matches(int row, Matcher<BibEntry> m) {
Optional<BibEntry> bibEntry = getBibEntry(row);
if (bibEntry.isPresent()) {
return m.matches(bibEntry.get());
}
return m.matches(null);
}
private boolean isComplete(int row) {
Optional<BibEntry> bibEntry = getBibEntry(row);
if (bibEntry.isPresent()) {
TypedBibEntry typedEntry = new TypedBibEntry(bibEntry.get(), panel.getBibDatabaseContext());
return typedEntry.hasAllRequiredFields();
}
return true;
}
private int isMarked(int row) {
Optional<BibEntry> bibEntry = getBibEntry(row);
if (bibEntry.isPresent()) {
return EntryMarker.isMarked(bibEntry.get());
}
return 0;
}
private Optional<BibEntry> getBibEntry(int row) {
try {
return Optional.of(model.getTableRows().get(row));
} catch (IndexOutOfBoundsException e) {
return Optional.empty();
}
}
public void scrollTo(int y) {
JScrollBar scb = pane.getVerticalScrollBar();
scb.setValue(y * scb.getUnitIncrement(1));
}
public void showFloatSearch() {
this.getTableModel().updateSearchState(MainTableDataModel.DisplayOption.FLOAT);
scrollTo(0);
}
/**
* Repaints the table with the most recent font configuration
*/
public void updateFont() {
setFont(GUIGlobals.currentFont);
int maxOfIconsAndFontSize = Math.max(GUIGlobals.currentFont.getSize(), Globals.prefs.getInt(JabRefPreferences.ICON_SIZE_SMALL));
setRowHeight(Globals.prefs.getInt(JabRefPreferences.TABLE_ROW_PADDING) + maxOfIconsAndFontSize);
// Update Table header with new settings
this.getTableHeader().setDefaultRenderer(new MainTableHeaderRenderer(this.getTableHeader().getDefaultRenderer()));
this.getTableHeader().resizeAndRepaint();
}
public void ensureVisible(int row) {
JScrollBar vert = pane.getVerticalScrollBar();
int y = row * getRowHeight();
if ((y < vert.getValue()) || ((y >= (vert.getValue() + vert.getVisibleAmount()))
&& (model.getSearchState() != MainTableDataModel.DisplayOption.FLOAT))) {
scrollToCenter(row, 1);
}
}
/**
* Ensures that the given entry is shown in the maintable.
* It also selects the given entry
* The execution is executed directly. Be sure that it happens in the EDT.
*
* @param entry the BibEntry to be shown
*/
public void ensureVisible(BibEntry entry) {
final int row = this.findEntry(entry);
if (row >= 0) {
if (this.getSelectedRowCount() == 0) {
this.setRowSelectionInterval(row, row);
}
this.ensureVisible(row);
}
}
public void scrollToCenter(int rowIndex, int vColIndex) {
if (!(this.getParent() instanceof JViewport)) {
return;
}
JViewport viewport = (JViewport) this.getParent();
// This rectangle is relative to the table where the
// northwest corner of cell (0,0) is always (0,0).
Rectangle rect = this.getCellRect(rowIndex, vColIndex, true);
// The location of the view relative to the table
Rectangle viewRect = viewport.getViewRect();
// Translate the cell location so that it is relative
// to the view, assuming the northwest corner of the
// view is (0,0).
rect.setLocation(rect.x - viewRect.x, rect.y - viewRect.y);
// Calculate location of rect if it were at the center of view
int centerX = (viewRect.width - rect.width) / 2;
int centerY = (viewRect.height - rect.height) / 2;
// Fake the location of the cell so that scrollRectToVisible
// will move the cell to the center
if (rect.x < centerX) {
centerX = -centerX;
}
if (rect.y < centerY) {
centerY = -centerY;
}
rect.translate(centerX, centerY);
// Scroll the area into view.
viewport.scrollRectToVisible(rect);
revalidate();
repaint();
}
public static void updateRenderers() {
MainTable.defRenderer = new GeneralRenderer(Globals.prefs.getColor(JabRefPreferences.TABLE_BACKGROUND),
Globals.prefs.getColor(JabRefPreferences.TABLE_TEXT));
Color sel = MainTable.defRenderer.getTableCellRendererComponent
(new JTable(), "", true, false, 0, 0).getBackground();
MainTable.reqRenderer = new GeneralRenderer(Globals.prefs.getColor(JabRefPreferences.TABLE_REQ_FIELD_BACKGROUND), Globals.prefs.getColor(JabRefPreferences.TABLE_TEXT));
MainTable.optRenderer = new GeneralRenderer(Globals.prefs.getColor(JabRefPreferences.TABLE_OPT_FIELD_BACKGROUND), Globals.prefs.getColor(JabRefPreferences.TABLE_TEXT));
MainTable.resolvedRenderer = new GeneralRenderer(
Globals.prefs.getColor(JabRefPreferences.TABLE_RESOLVED_FIELD_BACKGROUND),
Globals.prefs.getColor(JabRefPreferences.TABLE_TEXT));
MainTable.incRenderer = new IncompleteRenderer();
MainTable.compRenderer = new CompleteRenderer(Globals.prefs.getColor(JabRefPreferences.TABLE_BACKGROUND));
MainTable.grayedOutNumberRenderer = new CompleteRenderer(Globals.prefs.getColor(JabRefPreferences.GRAYED_OUT_BACKGROUND));
MainTable.veryGrayedOutNumberRenderer = new CompleteRenderer(Globals.prefs.getColor(JabRefPreferences.VERY_GRAYED_OUT_BACKGROUND));
MainTable.grayedOutRenderer = new GeneralRenderer(Globals.prefs.getColor(JabRefPreferences.GRAYED_OUT_BACKGROUND),
Globals.prefs.getColor(JabRefPreferences.GRAYED_OUT_TEXT), MainTable.mixColors(Globals.prefs.getColor(JabRefPreferences.GRAYED_OUT_BACKGROUND),
sel));
MainTable.veryGrayedOutRenderer = new GeneralRenderer(Globals.prefs.getColor(JabRefPreferences.VERY_GRAYED_OUT_BACKGROUND),
Globals.prefs.getColor(JabRefPreferences.VERY_GRAYED_OUT_TEXT), MainTable.mixColors(Globals.prefs.getColor(JabRefPreferences.VERY_GRAYED_OUT_BACKGROUND),
sel));
MainTable.markedRenderers = new ArrayList<>(EntryMarker.MARK_COLOR_LEVELS);
MainTable.markedNumberRenderers = new ArrayList<>(EntryMarker.MARK_COLOR_LEVELS);
for (int i = 0; i < EntryMarker.MARK_COLOR_LEVELS; i++) {
Color c = Globals.prefs.getColor(JabRefPreferences.MARKED_ENTRY_BACKGROUND + i);
MainTable.markedRenderers.add(new GeneralRenderer(c, Globals.prefs.getColor(JabRefPreferences.TABLE_TEXT),
MainTable.mixColors(Globals.prefs.getColor(JabRefPreferences.MARKED_ENTRY_BACKGROUND + i), sel)));
MainTable.markedNumberRenderers.add(new CompleteRenderer(c));
}
}
private static Color mixColors(Color one, Color two) {
return new Color((one.getRed() + two.getRed()) / 2, (one.getGreen() + two.getGreen()) / 2, (one.getBlue() + two.getBlue()) / 2);
}
private TableComparatorChooser<BibEntry> createTableComparatorChooser(JTable table, SortedList<BibEntry> list, Object sortingStrategy) {
return TableComparatorChooser.install(table, list, sortingStrategy);
}
/**
* Morten Alver: This override is a workaround NullPointerException when
* dragging stuff into the table. I found this in a forum, but have no idea
* why it works.
* @param newUI
*/
@Override
public void setUI(TableUI newUI) {
super.setUI(newUI);
TransferHandler handler = getTransferHandler();
setTransferHandler(null);
setTransferHandler(handler);
}
/**
* Find out which column is set as sort column.
* @param number The position in the sort hierarchy (primary, secondary, etc.)
* @return The sort column number.
*/
public int getSortingColumn(int number) {
List<Integer> l = comparatorChooser.getSortingColumns();
if (l.size() <= number) {
return -1;
} else {
return l.get(number);
}
}
public MainTableColumn getMainTableColumn(int modelIndex) {
return tableFormat.getTableColumn(modelIndex);
}
}