/*
* Copyright (C) 2011 Jason von Nieda <jason@vonnieda.org>
*
* This file is part of OpenPnP.
*
* OpenPnP is free software: you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* OpenPnP 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 General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with OpenPnP. If not, see
* <http://www.gnu.org/licenses/>.
*
* For more information about OpenPnP visit http://openpnp.org
*/
package org.openpnp.gui;
import java.awt.BorderLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.prefs.Preferences;
import java.util.regex.PatternSyntaxException;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableRowSorter;
import org.openpnp.events.FeederSelectedEvent;
import org.openpnp.gui.components.AutoSelectTextTable;
import org.openpnp.gui.components.ClassSelectionDialog;
import org.openpnp.gui.support.ActionGroup;
import org.openpnp.gui.support.Helpers;
import org.openpnp.gui.support.Icons;
import org.openpnp.gui.support.MessageBoxes;
import org.openpnp.gui.support.Wizard;
import org.openpnp.gui.support.WizardContainer;
import org.openpnp.gui.tablemodel.FeedersTableModel;
import org.openpnp.model.Configuration;
import org.openpnp.model.Location;
import org.openpnp.model.Part;
import org.openpnp.spi.Camera;
import org.openpnp.spi.Feeder;
import org.openpnp.spi.Nozzle;
import org.openpnp.util.MovableUtils;
import org.openpnp.util.UiUtils;
import org.pmw.tinylog.Logger;
import com.google.common.eventbus.Subscribe;
@SuppressWarnings("serial")
public class FeedersPanel extends JPanel implements WizardContainer {
private final Configuration configuration;
private final MainFrame mainFrame;
private static final String PREF_DIVIDER_POSITION = "FeedersPanel.dividerPosition";
private static final int PREF_DIVIDER_POSITION_DEF = -1;
private JTable table;
private FeedersTableModel tableModel;
private TableRowSorter<FeedersTableModel> tableSorter;
private JTextField searchTextField;
private JPanel configurationPanel;
private ActionGroup feederSelectedActionGroup;
private Preferences prefs = Preferences.userNodeForPackage(FeedersPanel.class);
public FeedersPanel(Configuration configuration, MainFrame mainFrame) {
this.configuration = configuration;
this.mainFrame = mainFrame;
setLayout(new BorderLayout(0, 0));
tableModel = new FeedersTableModel(configuration);
JPanel panel = new JPanel();
add(panel, BorderLayout.NORTH);
panel.setLayout(new BorderLayout(0, 0));
JToolBar toolBar = new JToolBar();
toolBar.setFloatable(false);
panel.add(toolBar, BorderLayout.CENTER);
JButton btnNewFeeder = new JButton(newFeederAction);
btnNewFeeder.setHideActionText(true);
toolBar.add(btnNewFeeder);
JButton btnDeleteFeeder = new JButton(deleteFeederAction);
btnDeleteFeeder.setHideActionText(true);
toolBar.add(btnDeleteFeeder);
toolBar.addSeparator();
toolBar.add(feedFeederAction);
toolBar.add(moveCameraToPickLocation);
toolBar.add(moveToolToPickLocation);
toolBar.add(pickFeederAction);
JPanel panel_1 = new JPanel();
panel.add(panel_1, BorderLayout.EAST);
JLabel lblSearch = new JLabel("Search");
panel_1.add(lblSearch);
searchTextField = new JTextField();
searchTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
search();
}
@Override
public void insertUpdate(DocumentEvent e) {
search();
}
@Override
public void changedUpdate(DocumentEvent e) {
search();
}
});
panel_1.add(searchTextField);
searchTextField.setColumns(15);
table = new AutoSelectTextTable(tableModel);
tableSorter = new TableRowSorter<>(tableModel);
final JSplitPane splitPane = new JSplitPane();
splitPane.setContinuousLayout(true);
splitPane
.setDividerLocation(prefs.getInt(PREF_DIVIDER_POSITION, PREF_DIVIDER_POSITION_DEF));
splitPane.addPropertyChangeListener("dividerLocation", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
prefs.putInt(PREF_DIVIDER_POSITION, splitPane.getDividerLocation());
}
});
add(splitPane, BorderLayout.CENTER);
splitPane.setLeftComponent(new JScrollPane(table));
table.setRowSorter(tableSorter);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
splitPane.setRightComponent(tabbedPane);
configurationPanel = new JPanel();
tabbedPane.addTab("Configuration", null, configurationPanel, null);
configurationPanel.setLayout(new BorderLayout(0, 0));
feederSelectedActionGroup = new ActionGroup(deleteFeederAction, feedFeederAction,
pickFeederAction, moveCameraToPickLocation, moveToolToPickLocation);
feederSelectedActionGroup.setEnabled(false);
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
Feeder feeder = getSelectedFeeder();
feederSelectedActionGroup.setEnabled(feeder != null);
configurationPanel.removeAll();
if (feeder != null) {
Wizard wizard = feeder.getConfigurationWizard();
if (wizard != null) {
wizard.setWizardContainer(FeedersPanel.this);
JPanel panel = wizard.getWizardPanel();
configurationPanel.add(panel);
}
}
revalidate();
repaint();
Configuration.get().getBus().post(new FeederSelectedEvent(feeder));
}
});
Configuration.get().getBus().register(this);
}
@Subscribe
public void feederSelected(FeederSelectedEvent event) {
SwingUtilities.invokeLater(() -> {
mainFrame.showTab("Feeders");
for (int i = 0; i < tableModel.getRowCount(); i++) {
if (tableModel.getFeeder(i) == event.feeder) {
table.getSelectionModel().setSelectionInterval(i, i);
table.scrollRectToVisible(new Rectangle(table.getCellRect(i, 0, true)));
break;
}
}
});
}
/**
* Activate the Feeders tab and show the Feeder for the specified Part. If none exists, prompt
* the user to create a new one.
*
* @param feeder
*/
public void showFeederForPart(Part part) {
mainFrame.showTab("Feeders");
Feeder feeder = findFeeder(part);
if (feeder == null) {
newFeeder(part);
}
else {
table.getSelectionModel().clearSelection();
for (int i = 0; i < tableModel.getRowCount(); i++) {
if (tableModel.getFeeder(i).getPart() == part) {
table.getSelectionModel().setSelectionInterval(0, i);
break;
}
}
}
}
private Feeder findFeeder(Part part) {
for (int i = 0; i < tableModel.getRowCount(); i++) {
if (tableModel.getFeeder(i).getPart() == part) {
return tableModel.getFeeder(i);
}
}
return null;
}
private Feeder getSelectedFeeder() {
int index = table.getSelectedRow();
if (index == -1) {
return null;
}
index = table.convertRowIndexToModel(index);
return tableModel.getFeeder(index);
}
private void search() {
RowFilter<FeedersTableModel, Object> rf = null;
// If current expression doesn't parse, don't update.
try {
rf = RowFilter.regexFilter("(?i)" + searchTextField.getText().trim());
}
catch (PatternSyntaxException e) {
Logger.warn("Search failed", e);
return;
}
tableSorter.setRowFilter(rf);
}
@Override
public void wizardCompleted(Wizard wizard) {
// Repaint the table so that any changed fields get updated.
table.repaint();
}
@Override
public void wizardCancelled(Wizard wizard) {}
private void newFeeder(Part part) {
if (Configuration.get().getParts().size() == 0) {
MessageBoxes.errorBox(getTopLevelAncestor(), "Error",
"There are currently no parts defined in the system. Please create at least one part before creating a feeder.");
return;
}
String title;
if (part == null) {
title = "Select Feeder...";
}
else {
title = "Select Feeder for " + part.getId() + "...";
}
ClassSelectionDialog<Feeder> dialog =
new ClassSelectionDialog<>(JOptionPane.getFrameForComponent(FeedersPanel.this),
title, "Please select a Feeder implemention from the list below.",
configuration.getMachine().getCompatibleFeederClasses());
dialog.setVisible(true);
Class<? extends Feeder> feederClass = dialog.getSelectedClass();
if (feederClass == null) {
return;
}
try {
Feeder feeder = feederClass.newInstance();
feeder.setPart(part == null ? Configuration.get().getParts().get(0) : part);
configuration.getMachine().addFeeder(feeder);
tableModel.refresh();
Helpers.selectLastTableRow(table);
}
catch (Exception e) {
MessageBoxes.errorBox(JOptionPane.getFrameForComponent(FeedersPanel.this),
"Feeder Error", e);
}
}
public Action newFeederAction = new AbstractAction() {
{
putValue(SMALL_ICON, Icons.add);
putValue(NAME, "New Feeder...");
putValue(SHORT_DESCRIPTION, "Create a new feeder.");
}
@Override
public void actionPerformed(ActionEvent arg0) {
newFeeder(null);
}
};
public Action deleteFeederAction = new AbstractAction() {
{
putValue(SMALL_ICON, Icons.delete);
putValue(NAME, "Delete Feeder");
putValue(SHORT_DESCRIPTION, "Delete the selected feeder.");
}
@Override
public void actionPerformed(ActionEvent arg0) {
int ret = JOptionPane.showConfirmDialog(getTopLevelAncestor(),
"Are you sure you want to delete " + getSelectedFeeder().getName() + "?",
"Delete " + getSelectedFeeder().getName() + "?", JOptionPane.YES_NO_OPTION);
if (ret == JOptionPane.YES_OPTION) {
configuration.getMachine().removeFeeder(getSelectedFeeder());
tableModel.refresh();
}
}
};
public Action feedFeederAction = new AbstractAction() {
{
putValue(SMALL_ICON, Icons.feed);
putValue(NAME, "Feed");
putValue(SHORT_DESCRIPTION, "Command the selected feeder to perform a feed operation.");
}
@Override
public void actionPerformed(ActionEvent arg0) {
new Thread() {
public void run() {
Feeder feeder = getSelectedFeeder();
Nozzle nozzle = MainFrame.get().getMachineControls().getSelectedNozzle();
try {
nozzle.moveToSafeZ();
feeder.feed(nozzle);
Location pickLocation = feeder.getPickLocation();
MovableUtils.moveToLocationAtSafeZ(nozzle, pickLocation);
}
catch (Exception e) {
MessageBoxes.errorBox(FeedersPanel.this, "Feed Error", e);
}
}
}.start();
}
};
public Action pickFeederAction = new AbstractAction() {
{
putValue(SMALL_ICON, Icons.load);
putValue(NAME, "Pick");
putValue(SHORT_DESCRIPTION, "Perform a feed and pick on the selected feeder.");
}
@Override
public void actionPerformed(ActionEvent arg0) {
new Thread() {
public void run() {
Feeder feeder = getSelectedFeeder();
Nozzle nozzle = MainFrame.get().getMachineControls().getSelectedNozzle();
try {
nozzle.moveToSafeZ();
feeder.feed(nozzle);
Location pickLocation = feeder.getPickLocation();
MovableUtils.moveToLocationAtSafeZ(nozzle, pickLocation);
nozzle.pick(feeder.getPart());
nozzle.moveToSafeZ();
}
catch (Exception e) {
MessageBoxes.errorBox(FeedersPanel.this, "Feed Error", e);
}
}
}.start();
}
};
public Action moveCameraToPickLocation = new AbstractAction() {
{
putValue(SMALL_ICON, Icons.centerCamera);
putValue(NAME, "Move Camera");
putValue(SHORT_DESCRIPTION,
"Move the camera to the selected feeder's current pick location.");
}
@Override
public void actionPerformed(ActionEvent arg0) {
UiUtils.submitUiMachineTask(() -> {
Feeder feeder = getSelectedFeeder();
Camera camera = MainFrame.get().getMachineControls().getSelectedTool().getHead()
.getDefaultCamera();
Location pickLocation = feeder.getPickLocation();
MovableUtils.moveToLocationAtSafeZ(camera, pickLocation);
});
}
};
public Action moveToolToPickLocation = new AbstractAction() {
{
putValue(SMALL_ICON, Icons.centerTool);
putValue(NAME, "Move Tool");
putValue(SHORT_DESCRIPTION,
"Move the tool to the selected feeder's current pick location.");
}
@Override
public void actionPerformed(ActionEvent arg0) {
new Thread() {
public void run() {
Feeder feeder = getSelectedFeeder();
Nozzle nozzle = MainFrame.get().getMachineControls().getSelectedNozzle();
try {
Location pickLocation = feeder.getPickLocation();
MovableUtils.moveToLocationAtSafeZ(nozzle, pickLocation);
}
catch (Exception e) {
MessageBoxes.errorBox(FeedersPanel.this, "Movement Error", e);
}
}
}.start();
}
};
}