WWZDataTablePane.java

/**
 * VStar: a statistical analysis tool for variable star data.
 * Copyright (C) 2009  AAVSO (http://www.aavso.org/)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */
package org.aavso.tools.vstar.ui.dialog.period.wwz;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableRowSorter;

import org.aavso.tools.vstar.data.ValidObservation;
import org.aavso.tools.vstar.ui.dialog.MessageBox;
import org.aavso.tools.vstar.ui.mediator.Mediator;
import org.aavso.tools.vstar.ui.mediator.message.PeriodAnalysisSelectionMessage;
import org.aavso.tools.vstar.ui.model.list.WWZDataTableModel;
import org.aavso.tools.vstar.util.IStartAndCleanup;
import org.aavso.tools.vstar.util.comparator.FormattedDoubleComparator;
import org.aavso.tools.vstar.util.locale.LocaleProps;
import org.aavso.tools.vstar.util.model.IModel;
import org.aavso.tools.vstar.util.model.WWZMultiperiodicModel;
import org.aavso.tools.vstar.util.notification.Listener;
import org.aavso.tools.vstar.util.period.wwz.WWZStatistic;

/**
 * This class represents a WWZ data table pane.
 */
@SuppressWarnings("serial")
public class WWZDataTablePane extends JPanel implements ListSelectionListener,
		IStartAndCleanup {

	private List<ValidObservation> obs;

	private JTable table;
	private WWZDataTableModel model;
	private TableRowSorter<WWZDataTableModel> rowSorter;

	private Listener<PeriodAnalysisSelectionMessage> periodAnalysisSelectionListener;

	protected JButton modelButton;

	/**
	 * Constructor
	 * 
	 * @param obs
	 *            The observations on which the WWZ was carried out.
	 * @param model
	 *            The WWZ table model.
	 */
	public WWZDataTablePane(WWZDataTableModel model) {
		super(new GridLayout(1, 1));

		this.obs = model.getWwt().getObs();
		this.model = model;

		table = new JTable(model);
		JScrollPane scrollPane = new JScrollPane(table);

		this.add(scrollPane);

		table.getSelectionModel().addListSelectionListener(this);

		table.setColumnSelectionAllowed(false);
		table.setRowSelectionAllowed(true);

		table.setAutoCreateRowSorter(true);
		FormattedDoubleComparator comparator = FormattedDoubleComparator
				.getInstance();
		rowSorter = new TableRowSorter<WWZDataTableModel>(model);
		for (int i = 0; i < model.getColumnCount(); i++) {
			rowSorter.setComparator(i, comparator);
		}
		table.setRowSorter(rowSorter);

		setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
		add(createButtonPanel());
	}

	protected JPanel createButtonPanel() {
		JPanel buttonPane = new JPanel();

		modelButton = new JButton(LocaleProps.get("CREATE_MODEL_BUTTON"));
		modelButton.setEnabled(false);
		modelButton.addActionListener(createModelButtonHandler());
		buttonPane.add(modelButton, BorderLayout.LINE_END);

		return buttonPane;
	}

	/**
	 * We send a period analysis selection message when the table selection
	 * value has "settled". This event could be consumed by other views such as
	 * plots.
	 */
	public void valueChanged(ListSelectionEvent e) {
		if (e.getSource() == table.getSelectionModel()
				&& table.getRowSelectionAllowed() && !e.getValueIsAdjusting()) {
			int row = table.getSelectedRow();

			if (row >= 0) {
				row = table.convertRowIndexToModel(row);

				PeriodAnalysisSelectionMessage message = new PeriodAnalysisSelectionMessage(
						this, model.getDataPointFromRow(row), row);
				message.setTag(Mediator.getParentDialogName(this));
				Mediator.getInstance().getPeriodAnalysisSelectionNotifier()
						.notifyListeners(message);
			}
		}
	}

	// Model button listener.
	private ActionListener createModelButtonHandler() {
		final JPanel parent = this;
		return new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				List<Double> periods = new ArrayList<Double>();
				int[] selectedTableRowIndices = table.getSelectedRows();
				for (int row : selectedTableRowIndices) {
					int modelRow = table.convertRowIndexToModel(row);
					WWZStatistic dataPoint = model
							.getDataPointFromRow(modelRow);
					periods.add(dataPoint.getPeriod());
				}

				if (!periods.isEmpty()) {
					try {
						IModel periodModel = new WWZMultiperiodicModel(model
								.getWwt(), periods);
						Mediator.getInstance().performModellingOperation(
								periodModel);
					} catch (Exception ex) {
						MessageBox.showErrorDialog(parent, "Modelling", ex
								.getLocalizedMessage());
					}
				}
			}
		};
	}

	/**
	 * Select the row in the table corresponding to the period analysis
	 * selection. We also enable the "refine" button.
	 */
	protected Listener<PeriodAnalysisSelectionMessage> createPeriodAnalysisListener() {
		final Component parent = this;

		return new Listener<PeriodAnalysisSelectionMessage>() {
			@Override
			public void update(PeriodAnalysisSelectionMessage info) {
				if (!Mediator.isMsgForDialog(Mediator.getParentDialog(WWZDataTablePane.this), info))
					return;
				if (info.getSource() != parent) {
					// Find data point in table.
					int row = -1;
					for (int i = 0; i < model.getRowCount(); i++) {
						if (model.getDataPointFromRow(i).equals(
								info.getDataPoint())) {
							row = i;
							break;
						}
					}

					// Note that the row may not correspond to anything in the
					// data table, e.g. in the case of period analysis
					// refinement or if a datapoint was selected from the full
					// set of data and this update method is being called in
					// the context of a maximal WWZ data set.
					if (row != -1) {
						// Convert to view index!
						row = table.convertRowIndexToView(row);

						// Scroll to an arbitrary column (zeroth) within
						// the selected row, then select that row.
						// Assumption: we are specifying the zeroth cell
						// within row i as an x,y coordinate relative to
						// the top of the table pane.
						// Note that we could call this on the scroll
						// pane, which would then forward the request to
						// the table pane anyway.
						int colWidth = (int) table.getCellRect(row, 0, true)
								.getWidth();
						int rowHeight = table.getRowHeight(row);
						table.scrollRectToVisible(new Rectangle(colWidth,
								rowHeight * row, colWidth, rowHeight));

						table.setRowSelectionInterval(row, row);
						enableButtons();
					}
				} else {
					enableButtons();
				}
			}

			@Override
			public boolean canBeRemoved() {
				return true;
			}
		};
	}

	/**
	 * Enable the buttons on this pane.
	 */
	protected void enableButtons() {
		modelButton.setEnabled(true);
	}

	@Override
	public void startup() {
		periodAnalysisSelectionListener = createPeriodAnalysisListener();
		Mediator.getInstance().getPeriodAnalysisSelectionNotifier()
				.addListener(periodAnalysisSelectionListener);
	}

	@Override
	public void cleanup() {
		Mediator.getInstance().getPeriodAnalysisSelectionNotifier()
				.removeListenerIfWilling(periodAnalysisSelectionListener);
	}
}