package com.ronald.gantengtimesheet.ui.timesheet;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.EventObject;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.text.JTextComponent;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.decorator.Highlighter;
import com.ronald.gantengtimesheet.config.Config;
import com.ronald.gantengtimesheet.db.RecordEntity;
import com.ronald.gantengtimesheet.db.RecordEntityCollection;
import com.ronald.gantengtimesheet.db.RecordEntityCollectionListener;
import com.ronald.gantengtimesheet.ui.timesheet.TimesheetTableModel.Columns;
@SuppressWarnings("serial")
public class TimesheetTable extends JXTable {
private TimesheetTableModel model = new TimesheetTableModel();
public TimesheetTable() {
setSortable(false);
setModel(model);
for (int i=0;i<getColumnCount()-1;i++) {
final TableColumn tc = getColumnModel().getColumn(i);
tc.setPreferredWidth(Columns.values()[i].getWidth());
tc.setMinWidth(Columns.values()[i].getWidth());
tc.setMaxWidth(Columns.values()[i].getWidth());
}
setCellSelectionEnabled(true);
setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
configureColumns();
RecordEntityCollection.getInstance().addListener(new RecordEntityCollectionListener() {
@Override
public void collectionChanged() {
reload();
}
});
// add popup
final JPopupMenu popup = new JPopupMenu();
final JMenuItem delete = new JMenuItem("Delete row(s)");
delete.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (int row : getSelectedRows()) {
int modelRow=convertRowIndexToModel(row);
if (modelRow < 0) { continue; }
final RecordEntity entity = model.data.get(modelRow);
entity.delete();
}
}
});
popup.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
int model = convertRowIndexToModel(getSelectedRow());
boolean enable = !(model < 0 || model == getRowCount() - 1);
delete.setEnabled(enable);
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { }
@Override
public void popupMenuCanceled(PopupMenuEvent e) { }
});
popup.add(delete);
setComponentPopupMenu(popup);
}
@Override
public boolean editCellAt(int row, int column, EventObject e) {
boolean result = super.editCellAt(row, column, e);
selectAll(e);
return result;
}
// select all text in the editor
private void selectAll(EventObject e) {
final Component editor = getEditorComponent();
if (editor == null || !(editor instanceof JTextComponent)) {
return;
}
((JTextComponent)editor).selectAll();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
((JTextComponent)editor).selectAll();
}
});
}
private final DecimalFormat formatter = new DecimalFormat("0.00");
private void configureColumns() {
// handle renderers and editors here
final NumberCellRenderer numberRenderer = new NumberCellRenderer();
getColumnModel().getColumn(TimesheetTableModel.Columns.NO.ordinal()).setCellRenderer(numberRenderer);
getColumnModel().getColumn(TimesheetTableModel.Columns.DURATION.ordinal()).setCellRenderer(numberRenderer);
getColumnModel().getColumn(TimesheetTableModel.Columns.RATE.ordinal()).setCellRenderer(new RateCellRenderer());
HighlightPredicate ratePredicate = new HighlightPredicate() {
@Override
public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
if (convertColumnIndexToModel(adapter.column) != TimesheetTableModel.Columns.RATE.ordinal()) {
return false;
}
return (adapter.getValueAt(adapter.row,adapter.column) == null);
}
};
Highlighter rateHighlighter = new ColorHighlighter(ratePredicate, null, Color.LIGHT_GRAY);
setHighlighters(rateHighlighter);
getColumnModel().getColumn(TimesheetTableModel.Columns.DURATION.ordinal()).setCellEditor(new NumberEditor());
getColumnModel().getColumn(TimesheetTableModel.Columns.RATE.ordinal()).setCellEditor(new NumberEditor());
}
SwingWorker<Void, Void> loadData;
public void reload() {
if (loadData != null && !loadData.isDone()) {
// cancel previous thread to avoid spamming the sql
loadData.cancel(true);
}
loadData = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
TimesheetComponent.getInstance().setBusy(true);
model.reload();
return null;
}
@Override
protected void done() {
int selectedRow = getSelectedRow();
model.fireTableDataChanged();
if (selectedRow > -1 && selectedRow < getRowCount()) {
getSelectionModel().setSelectionInterval(selectedRow, selectedRow);
}
TimesheetComponent.getInstance().setBusy(false);
}
};
loadData.execute();
}
@Override
public boolean isCellEditable(int row, int column) {
if (column == TimesheetTableModel.Columns.NO.ordinal()) {
return false;
}
if (row == model.getRowCount()-1) {
return false;
}
// do not allow cell editing if thread is still running
return (loadData == null || loadData.isDone());
}
protected RecordEntity getNewRecordEntity() {
return model.data.get(model.data.size()-1);
}
protected RecordEntity getLastRowRecordEntity() {
if (model.data.size()==1) {
return null;
}
return model.data.get(model.data.size()-2);
}
/**
* @author Ronald Suwandi
*
*/
private class RateCellRenderer extends NumberCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Color c = getForeground();
if (row < getRowCount()-1 && table.getValueAt(row,column) == null) {
// display default cell if value is empty
setText(""+Config.getDefaultRate());
}
return this;
}
}
private class NumberCellRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
final DefaultTableCellRenderer r = (DefaultTableCellRenderer) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
r.setHorizontalAlignment(JLabel.RIGHT);
if (value instanceof Double) {
r.setText(formatter.format(value));
}
if (row == getRowCount()-1) {
r.setFont(r.getFont().deriveFont(Font.BOLD));
}
return r;
}
}
}