/*
* Copyright (C) 2012 Dr. John Lindsay <jlindsay@uoguelph.ca>
*
* 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.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package rastercalculator;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Vector;
import javax.swing.*;
import whitebox.interfaces.ThreadListener;
import whitebox.interfaces.WhiteboxPluginHost;
import whitebox.structures.ExtensionFileFilter;
/**
*
* @author Dr. John Lindsay email: jlindsay@uoguelph.ca
*/
public class RasterCalculator extends JDialog implements ActionListener,
ThreadListener {
private String workingDirectory;
private JList list;
private JProgressBar progress = new JProgressBar(0, 100);
private JScrollPane jScrollPane1 = new JScrollPane();
private JScrollPane jScrollPane2 = new JScrollPane();;
private JTextArea textArea = new JTextArea();
private String pathSep = File.separator;
private WhiteboxPluginHost host = null;
private Map<String, String> images = new HashMap<>();
private Thread thread = null;
private ResourceBundle bundle;
public RasterCalculator(Frame owner, boolean modal, String workingDirectory) {
super(owner, modal);
this.setVisible(false);
this.workingDirectory = workingDirectory;
if (owner instanceof WhiteboxPluginHost) {
host = (WhiteboxPluginHost)owner;
bundle = host.getGuiLabelsBundle();
} else {
bundle = ResourceBundle.getBundle("rastercalculator.labels");
}
createGui();
}
public void reportProgress(int value) {
progress.setValue(value);
}
private JButton createButton(String buttonLabel, String toolTip) {
JButton btn = new JButton(buttonLabel);
btn.addActionListener(this);
btn.setActionCommand(buttonLabel);
btn.setToolTipText(toolTip);
//btn.setPreferredSize(new Dimension(width, 22));
return btn;
}
private JButton createButton(String buttonLabel) {
JButton btn = new JButton(buttonLabel);
btn.addActionListener(this);
btn.setActionCommand(buttonLabel);
//btn.setPreferredSize(new Dimension(width, 22));
return btn;
}
Map<String, StringIntPair> map = new HashMap<>();
private Vector getOtherFunctionsModel() {
Vector data = new Vector();
data.add("arccos()");
data.add("arcsin()");
data.add("arctan()");
data.add("cosh()");
data.add("delete([image],...)");
data.add("if(Cond,Then,Else)");
data.add("isNoData([image])");
data.add("MAX(,)");
data.add("MIN(,)");
data.add("negate()");
data.add("sinh()");
data.add("tanh()");
map.put("arccos()", new StringIntPair("arccos()", 1));
map.put("arcsin()", new StringIntPair("arcsin()", 1));
map.put("arctan()", new StringIntPair("arctan()", 1));
map.put("cosh()", new StringIntPair("cosh()", 1));
map.put("delete([image],...)", new StringIntPair("delete()", 1));
map.put("MAX(,)", new StringIntPair("MAX(,)", 2));
map.put("MIN(,)", new StringIntPair("MIN(,)", 2));
map.put("negate()", new StringIntPair("negate()", 1));
map.put("sinh()", new StringIntPair("sinh()", 1));
map.put("tanh()", new StringIntPair("tanh()", 1));
map.put("if(Cond,Then,Else)", new StringIntPair("if((Condition),Then,Else)", 21));
map.put("isNoData([image])", new StringIntPair("isNoData()", 1));
return data;
}
private void createGui() {
if (System.getProperty("os.name").contains("Mac")) {
this.getRootPane().putClientProperty("apple.awt.brushMetalLook", Boolean.TRUE);
}
setTitle(bundle.getString("RasterCalculator"));
// buttons
JButton btnExit = createButton(bundle.getString("Exit"));
JButton btnEvaluate = createButton(bundle.getString("Evaluate"));
JButton btnStop = createButton(bundle.getString("Stop"));
JButton btnHelp = createButton(bundle.getString("Help"));
JButton btnAddImage = createButton(bundle.getString("AddImage"));
JButton btnEqual = createButton("=", bundle.getString("Assignment"));
JButton btnClear = createButton(bundle.getString("Clear"));
JButton btnBackspace = createButton("DEL", bundle.getString("Backspace"));
JButton btnSqrBrackets = createButton("[ ]", bundle.getString("SquareBrackets"));
JButton btn1 = createButton("1");
JButton btn2 = createButton("2");
JButton btn3 = createButton("3");
JButton btn4 = createButton("4");
JButton btn5 = createButton("5");
JButton btn6 = createButton("6");
JButton btn7 = createButton("7");
JButton btn8 = createButton("8");
JButton btn9 = createButton("9");
JButton btn0 = createButton("0");
JButton btnDecimal = createButton(".");
JButton btnNegate = createButton("(-)", "Negate");
JButton btnPi = createButton("pi", "pi");
JButton btnBrackets = createButton("( )");
JButton btnAbs = createButton("abs", bundle.getString("AbsoluteValue"));
JButton btnDivision = createButton("\u00F7",
bundle.getString("FloatingDivision"));
JButton btnMultiplication = createButton("\u00D7",
bundle.getString("Multiplication"));
JButton btnAddition = createButton("+", bundle.getString("Addition"));
JButton btnSubtraction = createButton("\u2212", bundle.getString("Subtraction"));
JButton btnMod = createButton("%", "Modulo (Remainder)");
JButton btnIntDiv = createButton("\\", bundle.getString("IntegerDivision"));
JButton btnSqrt = createButton("\u221A", bundle.getString("SquareRoot"));
JButton btnSqr = createButton("sqr", bundle.getString("Square"));
JButton btnPow = createButton("pow", bundle.getString("Power"));
JButton btnSin = createButton("sin", "Sine");
JButton btnCos = createButton("cos", "Cosine");
JButton btnTan = createButton("tan", "Tangent");
JButton btnLog = createButton("log", "Base-10 Logarithm");
JButton btnLn = createButton("Ln", bundle.getString("NaturalLog"));
JButton btnExp = createButton("Exp", bundle.getString("Exponentiation"));
JButton btnEqualTo = createButton("==", bundle.getString("EqualTo"));
JButton btnNotEqualTo = createButton("!=", bundle.getString("NotEqualTo"));
JButton btnGreaterThan = createButton(">", bundle.getString("GreaterThan"));
JButton btnGreaterThanEqualTo = createButton(">=", bundle.getString("GreaterThanEqualTo"));
JButton btnLessThan = createButton("<", bundle.getString("LessThan"));
JButton btnLessThanEqualTo = createButton("<=", bundle.getString("LessThanEqualTo"));
JButton btnAnd = createButton("AND", bundle.getString("BooleanAND"));
JButton btnNot = createButton("NOT", bundle.getString("BooleanNOT"));
JButton btnOr = createButton("OR", bundle.getString("BooleanOR"));
JButton btnXor = createButton("XOR", bundle.getString("BooleanXOR"));
JPanel mainPane = new JPanel();
mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.Y_AXIS));
mainPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
JPanel topPane = new JPanel();
topPane.setLayout(new BoxLayout(topPane, BoxLayout.Y_AXIS));
topPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
Box topBox = Box.createHorizontalBox();
Box expressionBox = Box.createVerticalBox();
Box labelBox1 = Box.createHorizontalBox();
JLabel label1 = new JLabel("<html>" + bundle.getString("Expression") + " (e.g. <b><i>[Output Image] = [Image1] + [Image2]</i></b>)</html>"); //bundle.getString(
labelBox1.add(label1);
labelBox1.add(Box.createHorizontalGlue());
expressionBox.add(labelBox1);
expressionBox.add(Box.createVerticalStrut(4));
jScrollPane1 = new JScrollPane(textArea);
expressionBox.add(jScrollPane1);
expressionBox.setPreferredSize(new Dimension(10, 80));
topBox.add(expressionBox);
Box functionBox = Box.createVerticalBox();
Box labelBox2 = Box.createHorizontalBox();
JLabel label2 = new JLabel(bundle.getString("OtherFunctions") + ":");
labelBox2.add(label2);
labelBox2.add(Box.createHorizontalGlue());
functionBox.add(labelBox2);
functionBox.add(Box.createVerticalStrut(4));
Vector data = getOtherFunctionsModel();
list = new JList(data);
list.addMouseListener(new ActionJList(list, textArea, map));
jScrollPane2 = new JScrollPane(list);
functionBox.setPreferredSize(new Dimension(160, 10));
functionBox.setMaximumSize(new Dimension(160, 800));
functionBox.add(jScrollPane2);
functionBox.add(Box.createVerticalGlue());
topBox.add(Box.createHorizontalStrut(5));
topBox.add(functionBox);
topPane.add(topBox);
topPane.add(Box.createVerticalStrut(2));
Box addImageBox = Box.createHorizontalBox();
addImageBox.add(btnAddImage);
addImageBox.add(btnSqrBrackets);
addImageBox.add(btnEqual);
addImageBox.add(btnClear);
addImageBox.add(btnBackspace);
addImageBox.add(Box.createHorizontalGlue());
topPane.add(addImageBox);
mainPane.add(topPane);
JPanel buttonPane2 = new JPanel();
buttonPane2.setLayout(new BoxLayout(buttonPane2, BoxLayout.Y_AXIS));
buttonPane2.setBorder(BorderFactory.createLoweredBevelBorder());
JPanel buttonPane = new JPanel();
GridLayout layout = new GridLayout(0, 8);
buttonPane.setLayout(layout);
//buttonPane.setLayout(new GridLayout(8, 5));
//buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.Y_AXIS));
//buttonPane.setBorder(BorderFactory.createLoweredBevelBorder());
buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
buttonPane.add(btn7);
buttonPane.add(btn8);
buttonPane.add(btn9);
buttonPane.add(btnDivision);
buttonPane.add(btnIntDiv);
buttonPane.add(btnSin);
buttonPane.add(btnEqualTo);
buttonPane.add(btnNotEqualTo);
buttonPane.add(btn4);
buttonPane.add(btn5);
buttonPane.add(btn6);
buttonPane.add(btnMultiplication);
buttonPane.add(btnSqrt);
buttonPane.add(btnCos);
buttonPane.add(btnGreaterThan);
buttonPane.add(btnGreaterThanEqualTo);
buttonPane.add(btn1);
buttonPane.add(btn2);
buttonPane.add(btn3);
buttonPane.add(btnSubtraction);
buttonPane.add(btnSqr);
buttonPane.add(btnTan);
buttonPane.add(btnLessThan);
buttonPane.add(btnLessThanEqualTo);
buttonPane.add(btn0);
buttonPane.add(btnDecimal);
buttonPane.add(btnNegate);
buttonPane.add(btnAddition);
buttonPane.add(btnPow);
buttonPane.add(btnLog);
buttonPane.add(btnAnd);
buttonPane.add(btnNot);
buttonPane.add(btnPi);
buttonPane.add(btnBrackets);
buttonPane.add(btnAbs);
buttonPane.add(btnMod);
buttonPane.add(btnExp);
buttonPane.add(btnLn);
buttonPane.add(btnOr);
buttonPane.add(btnXor);
buttonPane2.add(buttonPane);
Box progressBox = Box.createHorizontalBox();
progressBox.add(Box.createHorizontalStrut(10));
JLabel label3 = new JLabel(bundle.getString("Progress") + ":");
progressBox.add(label3);
progressBox.add(Box.createHorizontalStrut(5));
progressBox.add(progress);
buttonPane2.add(progressBox);
progressBox.add(Box.createHorizontalStrut(10));
buttonPane2.add(Box.createVerticalStrut(10));
mainPane.add(buttonPane2);
JPanel bottomPane = new JPanel();
bottomPane.setLayout(new BoxLayout(bottomPane, BoxLayout.X_AXIS));
bottomPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
bottomPane.add(btnEvaluate);
bottomPane.add(btnStop);
bottomPane.add(Box.createHorizontalGlue());
bottomPane.add(btnExit);
bottomPane.add(btnHelp);
Container contentPane = getContentPane();
contentPane.add(mainPane, BorderLayout.CENTER);
contentPane.add(bottomPane, BorderLayout.SOUTH);
//contentPane.add(buttonPane, BorderLayout.PAGE_END);
pack();
//this.setVisible(true);
}
boolean firstImageSelected = false;
private void addImage() {
String str;
// set the filter.
ArrayList<ExtensionFileFilter> filters = new ArrayList<>();
String filterDescription = bundle.getString("RasterFiles") + " (*.dep)";
String[] extensions = {"DEP"};
ExtensionFileFilter eff = new ExtensionFileFilter(filterDescription, extensions);
filters.add(eff);
JFileChooser fc = new JFileChooser();
fc.setCurrentDirectory(new File(workingDirectory));
fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
fc.setMultiSelectionEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
for (int i = 0; i < filters.size(); i++) {
fc.setFileFilter(filters.get(i));
}
int result = fc.showOpenDialog(this);
File file = null;
if (result == JFileChooser.APPROVE_OPTION) {
file = fc.getSelectedFile();
String fileDirectory = file.getParentFile() + pathSep;
if (!firstImageSelected) {
workingDirectory = fileDirectory;
}
if (fileDirectory.equals(workingDirectory)) {
str = "[" + file.getName().replace(".dep", "") + "]";
} else {
str = "[" + file.toString() + "]";
}
textArea.insert(str, textArea.getCaretPosition());
textArea.requestFocus();
}
firstImageSelected = true;
}
private void cancelOperation() {
if (thread.isAlive()) {
//ProcessExpression pe = (ProcessExpression)thread;
thread.interrupt();
textArea.insert("\n" + bundle.getString("OperationCanceled") + "!", textArea.getText().length());
textArea.requestFocus();
}
}
@Override
public void actionPerformed(ActionEvent e) {
//Object source = e.getSource();
String ac = e.getActionCommand();
if (ac.equals("Evaluate")) {
String[] expressions = textArea.getText().split("\n");
processEquations(expressions);
} else if (ac.equals("Help")) {
if (host != null) {
String helpFile = host.getResourcesDirectory() + "Help/RasterCalculator.html";
host.showHelp(helpFile);
}
} else if (ac.equals("Exit")) {
this.dispose();
} else if (ac.equals("Stop")) {
cancelOperation();
} else if (ac.equals("[ ]")) {
textArea.insert("[]", textArea.getCaretPosition());
textArea.setCaretPosition(textArea.getCaretPosition() - 1);
textArea.requestFocus();
} else if (ac.equals("( )")) {
textArea.insert("()", textArea.getCaretPosition());
textArea.setCaretPosition(textArea.getCaretPosition() - 1);
textArea.requestFocus();
} else if (ac.equals("Add Image")) {
addImage();
} else if (ac.equals("Clear")) {
textArea.setText("");
textArea.requestFocus();
} else if (ac.equals("DEL")) {
String str = textArea.getText();
int pos = textArea.getCaretPosition();
if (pos > 0) {
str = str.substring(0, pos - 1) + str.substring(pos, str.length());
textArea.setText(str);
textArea.setCaretPosition(pos - 1);
}
textArea.requestFocus();
} else if (ac.toLowerCase().equals("pow")) {
textArea.insert("^", textArea.getCaretPosition());
textArea.requestFocus();
} else if (ac.toLowerCase().equals("sin") ||
ac.toLowerCase().equals("cos") ||
ac.toLowerCase().equals("tan") ||
ac.toLowerCase().equals("sqr") ||
ac.toLowerCase().equals("sqrt") ||
ac.toLowerCase().equals("log") ||
ac.toLowerCase().equals("ln") ||
ac.toLowerCase().equals("abs") ||
ac.toLowerCase().equals("exp") ||
ac.toLowerCase().equals("\u221A")){
textArea.insert(ac + "()", textArea.getCaretPosition());
textArea.setCaretPosition(textArea.getCaretPosition() - 1);
textArea.requestFocus();
} else if (ac.toLowerCase().equals("and") ||
ac.toLowerCase().equals("not")||
ac.toLowerCase().equals("or")||
ac.toLowerCase().equals("xor")) {
textArea.insert(ac + "(,)", textArea.getCaretPosition());
textArea.setCaretPosition(textArea.getCaretPosition() - 2);
textArea.requestFocus();
} else {
textArea.insert(ac, textArea.getCaretPosition());
textArea.requestFocus();
}
}
public void processEquations(String[] equations) {
try {
String expression;
boolean needToInsertOutputImage = false;
boolean expressionContainsImage = false;
boolean isDeleteExpression = false;
int a, b, c, i;
int imageNumber = 1;
boolean flag = false;
String imageName, imageAlias;
for (a = 0; a < equations.length; a++) {
// first trim the white-spaces from the expression
expression = equations[a].trim(); //.replace(" ", "");
needToInsertOutputImage = false;
expressionContainsImage = expression.contains("]");
if (expression.toLowerCase().contains("delete(") || expression.toLowerCase().contains("del(")) {
isDeleteExpression = true;
}
if (!expression.contains("]=") && expressionContainsImage
&& !isDeleteExpression) {
needToInsertOutputImage = true;
} else {
b = expression.indexOf("]=");
c = expression.indexOf("]==");
if (b == c && expressionContainsImage && !isDeleteExpression) {//it's an equality operation and not an assignment
needToInsertOutputImage = true;
}
}
if (needToInsertOutputImage) {
expression = "[" + getASuitableOutputFileName() + "]=" + expression;
}
//place all of the image names into the Images dictionary
b = -1;
flag = false;
do {
b = expression.indexOf("[", b + 1);
if (b == -1) {
flag = true;
} else {
//modExpressions.NumberOfImages += 1
c = expression.indexOf("]", b + 1);
imageName = expression.substring(b + 1, c);
imageAlias = "IMAGE" + imageNumber;
expression = expression.replace("[" + imageName + "]", imageAlias);
if (imageName.indexOf(pathSep) == -1) { //no directory has been specified
imageName = workingDirectory + imageName;
}
if (imageName.indexOf(".dep") == -1) {
imageName = imageName + ".dep";
}
images.put(imageAlias, imageName);
//modExpressions.ImageNames.Add(ImageAlias, ImageName)
imageNumber++;
}
} while (!flag);
// remove all spaces, which is safe to do because the image names have been removed.
//expression = equations[a].replace(" ", "");
ProcessExpression pe = new ProcessExpression(workingDirectory, expression, this);
pe.setImage(images);
thread = new Thread(pe);
thread.start();
}
//return true;
} catch (Exception e) {
System.out.print(e);
}
}
private String getASuitableOutputFileName() {
boolean flag = false;
String fileName = "";
int a = 0;
do {
a++;
fileName = workingDirectory + "Calc_Output_" + a + ".dep";
if (!(new File(fileName)).exists()) {
flag = true;
}
if (a == 1000) {
flag = true; //this is just in place to make sure that
//it doesn't encounter an endless loop
}
} while (!flag);
return fileName;
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
JFrame frame = new JFrame();
RasterCalculator rc = new RasterCalculator(frame, true, "");
}
@Override
public void notifyOfThreadComplete(Runnable thread) {
progress.setValue(0);
}
@Override
public void notifyOfProgress(int progressVal) {
progress.setValue(progressVal);
}
@Override
public void passOnThreadException(Exception e) {
if (e.getMessage() != null) {
textArea.setText(textArea.getText() + "\n" + e.getMessage());
} else {
textArea.setText(textArea.getText() + "\n" + e.toString());
}
}
@Override
public void notifyOfReturn(String returnValue) {
if (returnValue != null) {
if (returnValue.contains("IMAGE")) {
returnValue = images.get(returnValue);
}
if (!returnValue.contains(".dep")) {
textArea.setText(textArea.getText() + "\n" + returnValue);
} else {
if (host != null) {
host.returnData(returnValue);
} else {
textArea.setText(textArea.getText() + "\n" + returnValue);
}
}
}
}
@Override
public int showFeedback(String feedback) {
textArea.setText(textArea.getText() + "\n" + feedback);
return 0;
}
}
class ActionJList extends MouseAdapter {
protected JTextArea textArea;
protected JList list;
protected Map<String,StringIntPair> map;
public ActionJList(JList l, JTextArea text, Map<String,StringIntPair> map) {
list = l;
textArea = text;
this.map = map;
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int index = list.locationToIndex(e.getPoint());
ListModel dlm = list.getModel();
Object item = dlm.getElementAt(index);
list.ensureIndexIsVisible(index);
StringIntPair sip = (StringIntPair)map.get(item);
String str = sip.getString(); // + "()";
textArea.insert(str, textArea.getCaretPosition());
textArea.setCaretPosition(textArea.getCaretPosition() - sip.getVal());
textArea.requestFocus();
}
}
}
class StringIntPair {
private String string = "";
private int val = 0;
public StringIntPair(String s, int i) {
string = s;
val = i;
}
public String getString() {
return string;
}
public int getVal() {
return val;
}
}