package gdsc.smlm.ij.plugins;
/*-----------------------------------------------------------------------------
* GDSC SMLM Software
*
* Copyright (C) 2015 Alex Herbert
* Genome Damage and Stability Centre
* University of Sussex, UK
*
* This program 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.
*---------------------------------------------------------------------------*/
import gdsc.smlm.results.MemoryPeakResults;
import ij.IJ;
import ij.Macro;
import ij.WindowManager;
import ij.gui.GUI;
import ij.macro.Interpreter;
import ij.plugin.frame.Recorder;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.List;
import java.awt.Panel;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Locale;
/**
* Shows a list of all the results sets held in memory, allowing multiple results to be selected
*/
public class MultiDialog extends Dialog
implements ActionListener, KeyListener, WindowListener, MouseListener, ItemListener
{
private static final long serialVersionUID = -881270633231897572L;
private ArrayList<String> selected;
private Button cancel, okay, all, none;
private boolean wasCanceled;
private List list;
private String macroOptions;
private boolean macro;
/**
* Interface to allow a list of any type to be shown in the MultiDialog
*
* @author Alex Herbert
*/
public interface Items
{
/**
* Get the number of items to display.
*
* @return the size
*/
int size();
/**
* Gets the formatted name of the result for display in the dialog.
*
* @param i
* the result i
* @return the formatted name
*/
String getFormattedName(int i);
/**
* Removes the formatting from the name. The plain name will be in the list returned by
* {@link MultiDialog#getSelectedResults()}.
*
* @param formattedName
* the formatted name
* @return the plain name string
*/
String removeFormatting(String formattedName);
}
/**
* Base class for default implementation of the Items interface
*
* @author Alex Herbert
*/
public static abstract class BaseItems implements Items
{
/**
* Returns the same formatted name.
* <p>
* {@inheritDoc}
*
* @see gdsc.smlm.ij.plugins.MultiDialog.Items#removeFormatting(java.lang.String)
*/
public String removeFormatting(String formattedName)
{
return formattedName;
}
}
/**
* Class that allows the current results held in memory to be shown in the dialog
*
* @author Alex Herbert
*/
public static class MemoryResultsItems implements Items
{
private String[] names;
public MemoryResultsItems()
{
Collection<MemoryPeakResults> allResults = MemoryPeakResults.getAllResults();
names = new String[allResults.size()];
int i = 0;
for (MemoryPeakResults results : allResults)
names[i++] = ResultsManager.getName(results);
}
public int size()
{
return names.length;
}
public String getFormattedName(int i)
{
return names[i];
}
public String removeFormatting(String formattedName)
{
return ResultsManager.removeFormatting(formattedName);
}
}
private Items items;
public MultiDialog(String title, Items items)
{
super(WindowManager.getCurrentImage() != null ? (Frame) WindowManager.getCurrentImage().getWindow()
: IJ.getInstance() != null ? IJ.getInstance() : new Frame(), title, true);
addKeyListener(this);
addWindowListener(this);
macroOptions = Macro.getOptions();
macro = macroOptions != null;
this.items = items;
}
public void addSelected(ArrayList<String> selected)
{
this.selected = selected;
}
public void showDialog()
{
// Detect if running in a macro and just collect the input options
if (macro)
{
dispose();
}
else
{
add(buildPanels());
this.addKeyListener(this);
if (IJ.isMacintosh())
setResizable(false);
pack();
GUI.center(this);
setVisible(true);
IJ.wait(50); // work around for Sun/WinNT bug
}
}
protected Panel buildPanels()
{
Panel p = new Panel();
BorderLayout layout = new BorderLayout();
layout.setVgap(3);
p.setLayout(layout);
p.add(buildResultsList(), BorderLayout.NORTH, 0);
p.add(buildButtonPanel(), BorderLayout.CENTER, 1);
return p;
}
protected Component buildResultsList()
{
final int MAX_SIZE = 30;
int size = items.size();
int rows = (size < MAX_SIZE) ? size : MAX_SIZE;
list = new List(rows, true);
for (int n = 0; n < size; n++)
{
String formattedName = items.getFormattedName(n);
list.add(formattedName);
// Select the same as last time
if (selected != null && selected.contains(items.removeFormatting(formattedName)))
{
list.select(n);
}
}
list.addMouseListener(this);
list.addItemListener(this);
list.addKeyListener(this);
return (Component) list;
}
protected Panel buildButtonPanel()
{
Panel buttons = new Panel();
buttons.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0));
all = new Button("All");
all.addActionListener(this);
all.addKeyListener(this);
buttons.add(all);
none = new Button("None");
none.addActionListener(this);
none.addKeyListener(this);
buttons.add(none);
okay = new Button("OK");
okay.addActionListener(this);
okay.addKeyListener(this);
buttons.add(okay);
cancel = new Button("Cancel");
cancel.addActionListener(this);
cancel.addKeyListener(this);
buttons.add(cancel);
return buttons;
}
public boolean wasCanceled()
{
return wasCanceled;
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent e)
{
Object source = e.getSource();
if (source == okay || source == cancel)
{
wasCanceled = source == cancel;
dispose();
}
else if (source == all)
{
for (int i = 0; i < list.getItemCount(); i++)
list.select(i);
}
else if (source == none)
{
for (int i = 0; i < list.getItemCount(); i++)
list.deselect(i);
}
}
/*
* (non-Javadoc)
*
* @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
*/
public void keyTyped(KeyEvent paramKeyEvent)
{
}
public void keyPressed(KeyEvent e)
{
int keyCode = e.getKeyCode();
IJ.setKeyDown(keyCode);
if (keyCode == KeyEvent.VK_ENTER)
{
Object source = e.getSource();
if (source == okay || source == cancel || source == list)
{
wasCanceled = source == cancel;
dispose();
}
else if (source == all)
{
for (int i = 0; i < list.getItemCount(); i++)
list.select(i);
}
else if (source == none)
{
for (int i = 0; i < list.getItemCount(); i++)
list.deselect(i);
}
}
else if (keyCode == KeyEvent.VK_ESCAPE)
{
wasCanceled = true;
dispose();
IJ.resetEscape();
}
else if (keyCode == KeyEvent.VK_W &&
(e.getModifiers() & Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0)
{
wasCanceled = true;
dispose();
}
}
/*
* (non-Javadoc)
*
* @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
*/
public void keyReleased(KeyEvent paramKeyEvent)
{
}
public ArrayList<String> getSelectedResults()
{
ArrayList<String> selected;
// Get the selected names
if (macro)
{
selected = new ArrayList<String>();
String name = getValue("input");
while (name != null)
{
selected.add(name);
name = getValue("input" + selected.size());
}
}
else
{
final int[] listIndexes = list.getSelectedIndexes();
selected = new ArrayList<String>(listIndexes.length);
if (listIndexes.length > 0)
{
for (int index : listIndexes)
{
selected.add(items.removeFormatting(list.getItem(index)));
}
}
}
// Record as if we use the multiple_inputs option
if ((macro && Recorder.record && Recorder.recordInMacros) || Recorder.record)
{
if (!selected.isEmpty())
{
Recorder.recordOption("Input", selected.get(0));
if (selected.size() > 1)
{
Recorder.recordOption("Multiple_inputs");
for (int n = 1; n < selected.size(); ++n)
{
Recorder.recordOption("Input" + n, selected.get(n));
}
}
}
}
return selected;
}
/**
* Get a value from the macro options. Adapted from ij.gui.GenericDialog.
*
* @param label
* @return The value (or null)
*/
private String getValue(String label)
{
String theText = Macro.getValue(macroOptions, label, null);
if (theText != null && (theText.startsWith("&") || label.toLowerCase(Locale.US).startsWith(theText)))
{
// Is the value a macro variable?
if (theText.startsWith("&"))
theText = theText.substring(1);
Interpreter interp = Interpreter.getInstance();
String s = interp != null ? interp.getVariableAsString(theText) : null;
if (s != null)
theText = s;
}
return theText;
}
/*
* (non-Javadoc)
*
* @see java.awt.event.WindowListener#windowClosing(java.awt.event.WindowEvent)
*/
public void windowClosing(WindowEvent e)
{
wasCanceled = true;
dispose();
}
//@formatter:off
public void windowActivated(WindowEvent e)
{
}
public void windowOpened(WindowEvent e)
{
}
public void windowClosed(WindowEvent e)
{
}
public void windowIconified(WindowEvent e)
{
}
public void windowDeiconified(WindowEvent e)
{
}
public void windowDeactivated(WindowEvent e)
{
}
public void mousePressed(MouseEvent paramMouseEvent)
{
}
public void mouseReleased(MouseEvent paramMouseEvent)
{
}
public void mouseEntered(MouseEvent paramMouseEvent)
{
}
public void mouseExited(MouseEvent paramMouseEvent)
{
}
//@formatter:on
int lastIndex;
int modifiers;
int lastEvent = -1;
/*
* (non-Javadoc)
*
* @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
*/
public void mouseClicked(MouseEvent paramMouseEvent)
{
modifiers = paramMouseEvent.getModifiers();
}
/*
* (non-Javadoc)
*
* @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent)
*/
public void itemStateChanged(ItemEvent paramItemEvent)
{
int index = (Integer) paramItemEvent.getItem();
int event = paramItemEvent.getStateChange();
// If we have the shift key down, support multiple select/deselect
if (event == lastEvent && (modifiers & MouseEvent.SHIFT_MASK) != 0 &&
(event == ItemEvent.SELECTED || event == ItemEvent.DESELECTED))
{
if (lastIndex != index)
{
int top = Math.max(index, lastIndex);
int bottom = Math.min(index, lastIndex);
for (int i = bottom + 1; i < top; i++)
{
if (event == ItemEvent.SELECTED)
list.select(i);
else
list.deselect(i);
}
}
}
lastEvent = event;
lastIndex = index;
}
}