package com.ronald.gantengtimesheet.ui.timesheet;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JWindow;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.jdesktop.swingx.JXBusyLabel;
import org.jdesktop.swingx.JXButton;
import org.jdesktop.swingx.JXDatePicker;
import org.jdesktop.swingx.JXLabel;
import org.jdesktop.swingx.JXPanel;
import com.ronald.gantengtimesheet.config.Config;
import com.ronald.gantengtimesheet.db.ConfigEntity;
import com.ronald.gantengtimesheet.db.ConfigEntityCollection;
import com.ronald.gantengtimesheet.db.RecordEntity;
import com.ronald.gantengtimesheet.db.RecordEntityCollection;
import com.ronald.gantengtimesheet.report.Report;
import com.ronald.gantengtimesheet.ui.MainFrame;
import com.ronald.gantengtimesheet.ui.util.Util;
@SuppressWarnings("serial")
public final class TimesheetComponent extends JXPanel {
private static TimesheetComponent instance;
public static synchronized TimesheetComponent getInstance() {
if (instance == null) {
instance = new TimesheetComponent();
}
return instance;
}
private TimesheetComponent() {
assert instance == null;
instance = this;
setName("Timesheet");
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
addTablePanel();
addExportPanel();
addListeners();
}
private final JXPanel tablePanel = new JXPanel();
private final TimesheetTable table = new TimesheetTable();
private final JXDatePicker tableDatePicker = new JXDatePicker(new Date()); // current date
private final JXButton btnPrev = new JXButton(new ImageIcon("img/left.png"));
private final JXButton btnNext = new JXButton(new ImageIcon("img/right.png"));
private final JXButton btnTimer = new JXButton("Timer");
private final JXBusyLabel busyLabel = new JXBusyLabel(new Dimension(20,20));
{
btnTimer.setMnemonic('T');
busyLabel.setDelay(100);
tableDatePicker.setMinimumSize(tableDatePicker.getMaximumSize());
tableDatePicker.setFormats(Util.DateFormat);
btnPrev.setMinimumSize(new Dimension(22,22));
btnPrev.setPreferredSize(new Dimension(22,22));
btnPrev.setMaximumSize(new Dimension(22,22));
btnNext.setMinimumSize(new Dimension(22,22));
btnNext.setPreferredSize(new Dimension(22,22));
btnNext.setMaximumSize(new Dimension(22,22));
}
private void addTablePanel() {
setBusy(false);
tablePanel.setLayout(new BorderLayout());
tablePanel.setBorder(BorderFactory.createTitledBorder("Timesheet"));
final JPanel panel = new JPanel();
final GroupLayout group = new GroupLayout(panel);
panel.setLayout(group);
group.setHorizontalGroup(
group.createSequentialGroup().addComponent(btnPrev).addComponent(tableDatePicker).addComponent(btnNext)
.addComponent(btnTimer));
group.setVerticalGroup(
group.createParallelGroup().addComponent(btnPrev).addComponent(tableDatePicker).addComponent(btnNext)
.addComponent(btnTimer));
tablePanel.add(Util.createPanel(new FlowLayout(FlowLayout.CENTER), panel, busyLabel), BorderLayout.NORTH);
tablePanel.add(new JScrollPane(table));
table.reload();
tablePanel.setAlignmentY(TOP_ALIGNMENT);
add(tablePanel);
}
protected void setBusy(final boolean value) {
busyLabel.setEnabled(value);
busyLabel.setBusy(value);
}
protected Date getSelectedDate() {
return tableDatePicker.getDate();
}
private final JXPanel exportPanel = new JXPanel();
private final JXDatePicker fromDate = new JXDatePicker(new Date());
private final JXDatePicker toDate = new JXDatePicker(new Date());
{
fromDate.setMinimumSize(fromDate.getMaximumSize());
fromDate.setFormats(Util.DateFormat);
toDate.setMinimumSize(toDate.getMaximumSize());
toDate.setFormats(Util.DateFormat);
}
private final JXButton btnExport = new JXButton("Export To PDF");
private final JCheckBox chkSkipEmptyRecords = new JCheckBox("Skip Empty Records");
private final JTextField txtInvoiceNo = new JTextField();
{
txtInvoiceNo.setMinimumSize(new Dimension(120,20));
txtInvoiceNo.setPreferredSize(new Dimension(120,20));
txtInvoiceNo.setMaximumSize(new Dimension(200,20));
txtInvoiceNo.setText(Util.ReverseDateFormat.format(new Date()));
}
private void addExportPanel() {
final JXPanel datePanel = new JXPanel();
final GroupLayout groupLayout = new GroupLayout(datePanel);
groupLayout.setAutoCreateGaps(true);
datePanel.setLayout(groupLayout);
exportPanel.setLayout(new BoxLayout(exportPanel, BoxLayout.Y_AXIS));
exportPanel.setBorder(BorderFactory.createTitledBorder("Export Timesheet"));
final JLabel fromLabel = new JLabel("From");
final JLabel toLabel = new JLabel("To");
final JLabel invoiceLabel = new JLabel("Invoice No");
groupLayout.setHorizontalGroup(
groupLayout.createSequentialGroup()
.addGroup(groupLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(fromLabel)
.addComponent(toLabel))
.addGroup(groupLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(fromDate)
.addComponent(toDate)));
groupLayout.setVerticalGroup(
groupLayout.createSequentialGroup()
.addGroup(groupLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(fromLabel).addComponent(fromDate))
.addGroup(groupLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(toLabel).addComponent(toDate)));
// from date is -x days from current date (x is defined in config)
final Calendar c = Calendar.getInstance();
c.setTime(fromDate.getDate());
Integer days = Config.getDefaultReportDuration();
if (days == null) {
days = 0;
}
c.add(Calendar.DAY_OF_YEAR, -days);
fromDate.setDate(c.getTime());
datePanel.setAlignmentX(LEFT_ALIGNMENT);
btnExport.setMnemonic('x');
btnExport.setAlignmentX(LEFT_ALIGNMENT);
chkSkipEmptyRecords.setMnemonic('s');
chkSkipEmptyRecords.setAlignmentX(LEFT_ALIGNMENT);
chkSkipEmptyRecords.setSelected(true);
txtInvoiceNo.setAlignmentX(LEFT_ALIGNMENT);
invoiceLabel.setAlignmentX(LEFT_ALIGNMENT);
exportPanel.add(invoiceLabel);
exportPanel.add(txtInvoiceNo);
exportPanel.add(Box.createVerticalStrut(3));
exportPanel.add(datePanel);
exportPanel.add(chkSkipEmptyRecords);
exportPanel.add(btnExport);
exportPanel.setAlignmentY(TOP_ALIGNMENT);
add(exportPanel);
}
private TimerDialog timerDialog;
private void addListeners() {
btnTimer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
timerDialog = new TimerDialog();
timerDialog.setLocationRelativeTo(TimesheetComponent.this);
timerDialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(final WindowEvent e) {
timerDialog = null;
}
});
timerDialog.setVisible(true);
}
});
tableDatePicker.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
table.reload();
}
});
MainFrame.getInstance().addWindowStateListener(new WindowStateListener() {
@Override
public void windowStateChanged(final WindowEvent e) {
if (e.getNewState() == JFrame.NORMAL) {
// restore timer dialog if it's not disposed
if (timerDialog != null) {
timerDialog.setVisible(true);
}
}
}
});
btnExport.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
File lastDir = null;
if (Config.getLastDirectory() == null || Config.getLastDirectory().trim().isEmpty()) {
lastDir = new File(ConfigEntityCollection.Config.LAST_DIRECTORY.value);
} else {
lastDir = new File(Config.getLastDirectory());
}
if (!lastDir.exists() || !lastDir.isDirectory()) {
lastDir = new File(ConfigEntityCollection.Config.LAST_DIRECTORY.value);
}
final JFileChooser chooser = new JFileChooser(lastDir);
final File temp = new File("Invoice - " + Util.ReverseDateDashFormat.format(new Date())); // temp file
chooser.setSelectedFile(temp);
chooser.setFileFilter(new FileNameExtensionFilter(
"Portable Document Format (*.pdf)", "pdf"));
if (chooser.showSaveDialog(TimesheetComponent.this) == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile().getAbsoluteFile();
if (!file.getName().endsWith(".pdf")) {
file = new File(file.getAbsolutePath()+".pdf");
}
if (file.exists()) {
if (JOptionPane.showConfirmDialog(TimesheetComponent.this,
"File already exists. Overwrite?", "Confirmation",
JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
return;
}
}
exportPDF(file, chkSkipEmptyRecords.isSelected());
final ConfigEntity ce = ConfigEntityCollection.getInstance().getConfigEntity(
ConfigEntityCollection.Config.LAST_DIRECTORY.name);
ce.setValue(file.getParent());
ce.save();
}
}
});
final Calendar cal = Calendar.getInstance();
final ActionListener buttonListener = new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
cal.setTime(tableDatePicker.getDate());
if (e.getSource() == btnPrev) {
cal.add(Calendar.DAY_OF_YEAR, -1);
} else if (e.getSource() == btnNext) {
cal.add(Calendar.DAY_OF_YEAR, 1);
} else {
return;
}
tableDatePicker.setDate(cal.getTime());
try {
tableDatePicker.commitEdit();
} catch (final ParseException e1) {
e1.printStackTrace();
}
}
};
btnPrev.addActionListener(buttonListener);
btnNext.addActionListener(buttonListener);
}
public RecordEntity getLastRowRecordEntity() {
return table.getLastRowRecordEntity();
}
public RecordEntity getNewRecordEntity() {
return table.getNewRecordEntity();
}
SwingWorker<Void, Void> exportPDF;
private final JWindow processWindow = new JWindow();
private final JProgressBar progress = new JProgressBar();
private final JXButton btnCancelExport = new JXButton("Cancel");
private final JXLabel progressLabel = new JXLabel();
{
processWindow.setAlwaysOnTop(true);
processWindow.setLayout(new BorderLayout());
processWindow.add(progressLabel,BorderLayout.NORTH);
processWindow.add(Util.createBoxLayoutPanel(BoxLayout.X_AXIS, progress, btnCancelExport));
processWindow.setMinimumSize(new Dimension(300,70));
processWindow.getRootPane().setBorder(BorderFactory.createLineBorder(Color.BLACK));
btnCancelExport.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (exportPDF != null && !exportPDF.isDone()) {
exportPDF.cancel(true);
btnExport.setEnabled(true);
processWindow.setVisible(false);
}
}
});
}
private void exportPDF(final File output, final boolean skipEmptyRecords) {
if (exportPDF != null && !exportPDF.isDone()) {
// cancel previous thread to avoid spamming the sql
exportPDF.cancel(true);
}
exportPDF = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
btnExport.setEnabled(false);
processWindow.setVisible(true);
processWindow.setLocationRelativeTo(TimesheetComponent.this);
progress.setValue(0);
progressLabel.setText("Loading records from database");
final List<RecordEntity> result = RecordEntityCollection.getInstance().getRecordEntities(
fromDate.getDate(), toDate.getDate());
progress.setValue(100);
progress.setValue(0);
progressLabel.setText("Exporting records to pdf");
Report.getInstance().exportToPDF(output, txtInvoiceNo.getText(), fromDate.getDate(), toDate.getDate(),
result, skipEmptyRecords, progress, progressLabel);
progress.setValue(100);
return null;
}
@Override
protected void done() {
btnExport.setEnabled(true);
processWindow.setVisible(false);
try {
Desktop.getDesktop().open(output);
} catch (final IOException e) {
e.printStackTrace();
}
}
};
exportPDF.execute();
}
}