/* * 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 whiteboxgis.user_interfaces; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Frame; import java.awt.Toolkit; 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.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.ResourceBundle; import java.util.logging.Level; import javax.swing.*; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import whitebox.interfaces.Communicator; import whitebox.interfaces.DialogComponent; import whitebox.utilities.FileUtilities; /** * * @author Dr. John Lindsay email: jlindsay@uoguelph.ca */ public class ToolDialog extends JDialog implements Communicator, ActionListener, HyperlinkListener { private JButton ok; private JButton close; private JButton viewCode; private JButton back = new JButton(); private JButton forward = new JButton(); private JButton newHelp; private JButton modifyHelp; private JEditorPane helpPane = new JEditorPane(); private JScrollPane mainScrollPane = null; //new JScrollPane(); private JPanel mainPanel = new JPanel(); private String helpFile = ""; private String parameterFile = ""; private String graphicsDirectory = ""; private String workingDirectory = ""; private String applicationDirectory = ""; private String resourcesDirectory = ""; private String logDirectory = ""; private String pluginName = ""; private String pathSep = ""; private String sourceFile = ""; private ArrayList<DialogComponent> components = new ArrayList<>(); private Communicator host = null; private boolean automaticallyClose = true; private ArrayList<String> helpHistory = new ArrayList<>(); private int helpHistoryIndex = 0; private ResourceBundle bundle; private ResourceBundle messages; public ToolDialog(Frame owner, boolean modal, String pluginName, String title, String helpFile) { super(owner, modal); pathSep = File.separator; host = (Communicator) owner; workingDirectory = host.getWorkingDirectory(); this.helpFile = helpFile; this.helpHistory.add(helpFile); this.pluginName = pluginName; applicationDirectory = host.getApplicationDirectory(); resourcesDirectory = host.getResourcesDirectory(); parameterFile = FileUtilities.findFileInDirectory(new File(resourcesDirectory + "plugins" + pathSep), pluginName + ".xml"); // parameterFile = resourcesDirectory + "plugins" // + pathSep + "Dialogs" + pathSep + pluginName + ".xml"; // see if the parameterFile exists. bundle = host.getGuiLabelsBundle(); messages = host.getMessageBundle(); if (!(new File(parameterFile).exists())) { logException("ParameterFileNotLocated", new Exception("ParameterFileNotLocated")); host.showFeedback(messages.getString("ParameterFileNotLocated")); } graphicsDirectory = resourcesDirectory + "Images" + pathSep; createGui(title); } private void createGui(String title) { if (System.getProperty("os.name").contains("Mac")) { this.getRootPane().putClientProperty("apple.awt.brushMetalLook", Boolean.TRUE); } String imgLocation = null; ImageIcon image = null; createPopupMenus(); ok = new JButton(bundle.getString("Run")); close = new JButton(bundle.getString("Close")); viewCode = new JButton(bundle.getString("ViewCode")); newHelp = new JButton(bundle.getString("NewHelp")); modifyHelp = new JButton(bundle.getString("ModifyHelp")); helpPane.addHyperlinkListener(this); helpPane.setContentType("text/html"); drawMainPanel(); //mainScrollPane.setMinimumSize(new Dimension(380, 100)); //JScrollPane helpScroll = new JScrollPane(helpPane); Box box2 = Box.createHorizontalBox(); box2.add(Box.createHorizontalStrut(10)); box2.add(ok); ok.setActionCommand("ok"); ok.addActionListener(this); //box2.add(Box.createRigidArea(new Dimension(5, 30))); box2.add(Box.createHorizontalStrut(5)); box2.add(close); close.setActionCommand("close"); close.addActionListener(this); box2.add(Box.createHorizontalStrut(5)); File sourceFileDir = new File(resourcesDirectory + "plugins" + pathSep + "source_files"); // + pathSep + pluginName + ".java"; findSourceFile(sourceFileDir); // see if the source file exists. if (sourceFile.length() > 0) { viewCode.setActionCommand("viewCode"); viewCode.addActionListener(this); box2.add(viewCode); } box2.add(Box.createHorizontalGlue()); // create the newHelp button newHelp.setActionCommand("newHelp"); newHelp.setToolTipText(messages.getString("CreateNewHelpEntry")); newHelp.addActionListener(this); newHelp.setVisible(false); box2.add(newHelp); box2.add(Box.createHorizontalStrut(5)); // create the newHelp button modifyHelp.setActionCommand("modifyHelp"); modifyHelp.setToolTipText(messages.getString("ModifyHelp")); modifyHelp.addActionListener(this); modifyHelp.setVisible(false); box2.add(modifyHelp); box2.add(Box.createHorizontalStrut(5)); // create the back button imgLocation = graphicsDirectory + "HelpBack.png"; image = new ImageIcon(imgLocation, ""); back.setActionCommand("back"); back.setToolTipText(bundle.getString("Back")); back.addActionListener(this); try { back.setIcon(image); } catch (Exception e) { back.setText("<"); } box2.add(back); box2.add(Box.createHorizontalStrut(5)); // create the forward button imgLocation = graphicsDirectory + "HelpForward.png"; image = new ImageIcon(imgLocation, ""); forward.setActionCommand("forward"); forward.setToolTipText(bundle.getString("Forward")); forward.addActionListener(this); try { forward.setIcon(image); } catch (Exception e) { forward.setText(">"); } box2.add(forward); box2.add(Box.createHorizontalStrut(10)); JScrollPane helpScroll = new JScrollPane(helpPane); JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mainScrollPane, helpScroll); // helpScroll); //mainScrollPane, helpScroll); splitter.setDividerLocation(365); splitter.setResizeWeight(0.0); splitter.setDividerSize(4); // Box box3 = Box.createHorizontalBox(); // box3.add(mainScrollPane); // JScrollPane helpScroll = new JScrollPane(helpPane); // box3.add(helpScroll); this.getContentPane().add(splitter, BorderLayout.CENTER); //this.getContentPane().add(box3, BorderLayout.CENTER); this.getContentPane().add(box2, BorderLayout.SOUTH); File hlp = new File(helpFile); if (!hlp.exists()) { // use the NoHelp.html file. helpFile = resourcesDirectory + "Help" + pathSep + "other" + pathSep + "NoHelp.html"; newHelp.setVisible(true); } else { modifyHelp.setVisible(true); } helpPane.setEditable(false); try { //URL helpURL = getClass().getResource(helpFile); //helpPane.setPage(helpURL); helpPane.setPage(new URL("file:///" + helpFile)); //helpPane.navigate(helpFile); } catch (IOException e) { System.err.println(e.getMessage()); } helpPane.addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { helpMousePress(e); } }); //mainScrollPane.setSize(750, 750); setTitle(title); pack(); // Centre the dialog on the screen. // Get the size of the screen Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); int screenHeight = dim.height; int screenWidth = dim.width; int myWidth = 800; //this.getWidth(); int myHeight = 400; //this.getHeight(); setLocation((int) (screenWidth / 2.0 - myWidth / 2.0), (int) (screenHeight / 2.0 - myHeight / 2.0)); // setLocationRelativeTo(null); } private JPopupMenu helpPopup; private void createPopupMenus() { helpPopup = new JPopupMenu(); JMenuItem mi = new JMenuItem(bundle.getString("ViewHelpFileSource")); mi.addActionListener(this); mi.setActionCommand("viewHelpFileSource"); helpPopup.add(mi); } private void drawMainPanel() { try { mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS)); String[] args; File file = new File(parameterFile); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(file); doc.getDocumentElement().normalize(); Node topNode = doc.getFirstChild(); Element docElement = doc.getDocumentElement(); NodeList nl = docElement.getElementsByTagName("DialogComponent"); String componentType; if (nl != null && nl.getLength() > 0) { for (int i = 0; i < nl.getLength(); i++) { Element el = (Element) nl.item(i); componentType = el.getAttribute("type"); if (componentType.equals("DialogFile")) { args = new String[7]; args[0] = getTextValue(el, "Name"); args[1] = getTextValue(el, "Description"); args[2] = getTextValue(el, "LabelText"); if (args[2].toLowerCase().replace(":", "").equals("input raster file")) { args[2] = bundle.getString("InputRaster"); } else if (args[2].toLowerCase().replace(":", "").equals("output raster file")) { args[2] = bundle.getString("OutputRaster"); } else if (args[2].toLowerCase().replace(":", "").equals("input vector file")) { args[2] = bundle.getString("InputVector"); } else if (args[2].toLowerCase().replace(":", "").equals("output vector file")) { args[2] = bundle.getString("OutputVector"); } String dialogMode = getTextValue(el, "DialogMode"); if (dialogMode.toLowerCase().contains("open")) { args[3] = Integer.toString(DialogFile.MODE_OPEN); } else { args[3] = Integer.toString(DialogFile.MODE_SAVEAS); } args[4] = getTextValue(el, "ShowButton").toLowerCase(); args[5] = getTextValue(el, "Filter"); args[6] = getTextValue(el, "MakeOptional").toLowerCase(); DialogFile df = new DialogFile(this); df.setArgs(args); df.setTextFieldActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { okPressed(); } }); components.add(df); mainPanel.add(df); //box.add(df); } else if (componentType.equals("DialogMultiFile")) { args = new String[4]; args[0] = getTextValue(el, "Name"); args[1] = getTextValue(el, "Description"); args[2] = getTextValue(el, "LabelText"); args[3] = getTextValue(el, "Filter"); DialogMultiFile dmf = new DialogMultiFile(this); dmf.setArgs(args); components.add(dmf); mainPanel.add(dmf); //box.add(dmf); } else if (componentType.equals("DialogCheckBox")) { args = new String[4]; args[0] = getTextValue(el, "Name"); args[1] = getTextValue(el, "Description"); args[2] = getTextValue(el, "LabelText"); args[3] = getTextValue(el, "InitialState").toLowerCase(); DialogCheckBox dc = new DialogCheckBox(); dc.setArgs(args); components.add(dc); mainPanel.add(dc); //box.add(dc); } else if (componentType.equals("DialogComboBox")) { args = new String[5]; args[0] = getTextValue(el, "Name"); args[1] = getTextValue(el, "Description"); args[2] = getTextValue(el, "LabelText"); args[3] = getTextValue(el, "ListItems"); args[4] = getTextValue(el, "DefaultItem"); DialogComboBox cb = new DialogComboBox(); cb.setArgs(args); components.add(cb); mainPanel.add(cb); //box.add(cb); } else if (componentType.equals("DialogFieldSelector")) { args = new String[4]; args[0] = getTextValue(el, "Name"); args[1] = getTextValue(el, "Description"); args[2] = getTextValue(el, "LabelText"); args[3] = getTextValue(el, "Multiselection"); DialogFieldSelector fs = new DialogFieldSelector(); fs.setHostDialog(this); fs.setArgs(args); components.add(fs); mainPanel.add(fs); } else if (componentType.equals("DialogDataInput")) { args = new String[6]; args[0] = getTextValue(el, "Name"); args[1] = getTextValue(el, "Description"); args[2] = getTextValue(el, "LabelText"); args[3] = getTextValue(el, "InitialText"); args[4] = getTextValue(el, "NumericalInputOnly").toLowerCase(); args[5] = getTextValue(el, "MakeOptional").toLowerCase(); DialogDataInput di = new DialogDataInput(); di.setArgs(args); components.add(di); mainPanel.add(di); //box.add(di); } else if (componentType.equals("DialogOption")) { args = new String[5]; args[0] = getTextValue(el, "Name"); args[1] = getTextValue(el, "Description"); args[2] = getTextValue(el, "LabelText"); args[3] = getTextValue(el, "Button1Label"); args[4] = getTextValue(el, "Button2Label"); DialogOption opt = new DialogOption(); opt.setArgs(args); components.add(opt); mainPanel.add(opt); //box.add(opt); } else if (componentType.equals("DialogReclassGrid")) { args = new String[2]; args[0] = getTextValue(el, "Name"); args[1] = getTextValue(el, "Description"); DialogReclassGrid drg = new DialogReclassGrid(this); drg.setArgs(args); components.add(drg); mainPanel.add(drg); } else if (componentType.equals("DialogWeightedMultiFile")) { args = new String[4]; args[0] = getTextValue(el, "Name"); args[1] = getTextValue(el, "Description"); args[2] = getTextValue(el, "ShowCheck"); args[3] = getTextValue(el, "Filter"); DialogWeightedMultiFile dwmf = new DialogWeightedMultiFile(this); dwmf.setArgs(args); components.add(dwmf); mainPanel.add(dwmf); } else if (componentType.equals("Label")) { args = new String[2]; args[0] = getTextValue(el, "Name"); args[1] = getTextValue(el, "LabelText"); Box box = Box.createHorizontalBox(); JLabel lbl = new JLabel(args[1]); box.add(Box.createHorizontalStrut(5)); box.add(lbl); box.add(Box.createHorizontalGlue()); mainPanel.add(box); } } } //box.setBackground(Color.DARK_GRAY); //mainPanel.add(box); mainScrollPane = new JScrollPane(mainPanel); } catch (ParserConfigurationException | SAXException | IOException e) { logException("ToolDialog.drawMainPanel", e); showFeedback(e.getMessage()); } } private String getTextValue(Element ele, String tagName) { String textVal = ""; try { NodeList nl = ele.getElementsByTagName(tagName); if (nl != null && nl.getLength() > 0) { Element el = (Element) nl.item(0); textVal = el.getFirstChild().getNodeValue(); } } catch (Exception e) { } finally { return textVal; } } private String[] collectValues() { int numComponents = components.size(); String[] ret = new String[numComponents]; for (int i = 0; i < numComponents; i++) { ret[i] = components.get(i).getValue(); } return ret; } @Override public String getWorkingDirectory() { // update the workingDirectory workingDirectory = host.getWorkingDirectory(); return workingDirectory; } @Override public void setWorkingDirectory(String workingDirectory) { this.workingDirectory = workingDirectory; // update the workingDirectory host.setWorkingDirectory(workingDirectory); } @Override public String getApplicationDirectory() { // update the applicationDirectory applicationDirectory = host.getApplicationDirectory(); return applicationDirectory; } @Override public void setApplicationDirectory(String applicationDirectory) { this.applicationDirectory = applicationDirectory; host.setApplicationDirectory(applicationDirectory); } @Override public String getResourcesDirectory() { // update the applicationDirectory resourcesDirectory = host.getResourcesDirectory(); return resourcesDirectory; } @Override public String getLogDirectory() { logDirectory = host.getLogDirectory(); return logDirectory; } /** * Used to communicate feedback pop-up messages between a plugin tool and * the main Whitebox user-interface. * * @param feedback String containing the text to display. */ @Override public int showFeedback(String message) { host.showFeedback(message); return -1; } @Override public int showFeedback(String message, int optionType, int messageType) { int n = host.showFeedback(message, optionType, messageType); return n; } /** * Used to find whether the dialog will automatically close after being * launched. * * @return boolean. */ public boolean getAutomaticallyClose() { return automaticallyClose; } /** * Used to set whether the dialog should automatically close after bing * launched. * * @param value Boolean value. True if dialog should close, otherwise false. */ public void setAutomaticallyClose(boolean value) { automaticallyClose = value; } /** * Used to run a plugin through the Host app. * @param pluginName String containing the descriptive name of the plugin. * @param args String array containing the parameters to feed to the plugin. * @param runOnDedicatedThread boolean value; set to true if the tool should * be run on a dedicated thread and false if it should be run on the * same thread as the calling Communicator. */ @Override public void runPlugin(String pluginName, String[] args, boolean runOnDedicatedThread) { host.runPlugin(pluginName, args, runOnDedicatedThread); if (automaticallyClose) { //this.setVisible(false); this.dispose(); } } /** * Used to run a plugin through the Host app. * * @param pluginName String containing the descriptive name of the plugin. * @param args String array containing the parameters to feed to the plugin. */ @Override public void runPlugin(String pluginName, String[] args) { host.runPlugin(pluginName, args); if (automaticallyClose) { //this.setVisible(false); this.dispose(); } } private void back() { // helpPane.back(); if (helpHistoryIndex == 0) { return; } helpHistoryIndex--; try { helpPane.setPage("file:" + helpHistory.get(helpHistoryIndex)); } catch (IOException e) { System.err.println(e.getStackTrace()); } } private void forward() { // helpPane.forward(); if (helpHistoryIndex == helpHistory.size() - 1) { return; } helpHistoryIndex++; try { helpPane.setPage("file:" + helpHistory.get(helpHistoryIndex)); } catch (IOException e) { System.err.println(e.getStackTrace()); } } private void findSourceFile(File dir) { File[] files = dir.listFiles(); for (int x = 0; x < files.length; x++) { if (files[x].isDirectory()) { findSourceFile(files[x]); } else if (files[x].toString().contains(pluginName + ".java")) { sourceFile = files[x].toString(); break; } } } private void okPressed() { String[] args = collectValues(); boolean containsNull = false; for (int i = 0; i < args.length; i++) { if (args[i] == null) { containsNull = true; showFeedback(messages.getString("Parameter") + " " + components.get(i).getComponentName() + " " + messages.getString("ParameterNotSpecified")); break; } } if (!containsNull) { runPlugin(pluginName, args); } } private void helpMousePress(MouseEvent e) { //int selRow = layersTree.getRowForLocation(e.getX(), e.getY()); //TreePath selPath = layersTree.getPathForLocation(e.getX(), e.getY()); //String label; if (e.getButton() == 3 || e.isPopupTrigger()) { helpPopup.show((JComponent) e.getSource(), e.getX(), e.getY()); } } private void newHelp() { String helpDirectory = host.getResourcesDirectory() + "Help" + pathSep; String fileName = helpDirectory + pluginName + ".html"; // grab the text within the "NewHelp.txt" file in the helpDirectory; String defaultHelp = helpDirectory + "NewHelp.txt"; if (!(new File(defaultHelp)).exists()) { showFeedback(messages.getString("NoHelp")); return; } try { String defaultText = FileUtilities.readFileAsString(defaultHelp); // now place this text into the new file. FileUtilities.fillFileWithString(fileName, defaultText); ViewCodeDialog vcd = new ViewCodeDialog((Frame) host, false, new File(fileName), true); vcd.setSize(new Dimension(800, 600)); vcd.setVisible(true); } catch (IOException ioe) { showFeedback(messages.getString("HelpNotRead")); return; } } private void modifyHelp() { String helpDirectory = host.getResourcesDirectory() + "Help" + pathSep; String fileName = helpDirectory + pluginName + ".html"; // grab the text within the "NewHelp.txt" file in the helpDirectory; if (!(new File(fileName)).exists()) { showFeedback(messages.getString("NoHelpDirectory")); return; } ViewCodeDialog vcd = new ViewCodeDialog((Frame) host, false, new File(fileName), true); vcd.setSize(new Dimension(800, 600)); vcd.setVisible(true); } @Override public void actionPerformed(ActionEvent e) { Object source = e.getSource(); String actionCommand = e.getActionCommand(); if (actionCommand.equals("close")) { this.dispose(); } else if (actionCommand.equals("ok")) { okPressed(); } else if (actionCommand.equals("viewCode")) { ViewCodeDialog vcd = new ViewCodeDialog((Frame) host, false, pluginName, this.getTitle()); vcd.setSize(new Dimension(800, 600)); vcd.setVisible(true); } else if (actionCommand.equals("back")) { back(); } else if (actionCommand.equals("forward")) { forward(); } else if (actionCommand.equals("viewHelpFileSource")) { ViewCodeDialog vcd = new ViewCodeDialog((Frame) host, false, new File(helpFile), true); vcd.setSize(new Dimension(800, 600)); vcd.setVisible(true); } else if (actionCommand.equals("newHelp")) { newHelp(); } else if (actionCommand.equals("modifyHelp")) { modifyHelp(); } } @Override public void hyperlinkUpdate(HyperlinkEvent event) { if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { try { if (helpHistoryIndex == helpHistory.size() - 1) { helpHistory.add(event.getURL().getFile()); helpHistoryIndex = helpHistory.size() - 1; } else { for (int i = helpHistory.size() - 1; i > helpHistoryIndex; i--) { helpHistory.remove(i); } helpHistory.add(event.getURL().getFile()); helpHistoryIndex = helpHistory.size() - 1; } helpPane.setPage(event.getURL()); } catch (IOException ioe) { // Some warning to user } } } @Override public void dispose() { ok = null; close = null; viewCode = null; back = null; forward = null; helpPane = null; mainScrollPane = null; mainPanel = null; components = null; host = null; super.dispose(); } @Override public ResourceBundle getGuiLabelsBundle() { return bundle; } @Override public ResourceBundle getMessageBundle() { return messages; } @Override public void logException(String message, Exception e) { if (host != null) { host.logException(message, e); } } @Override public void logThrowable(String message, Throwable t) { if (host != null) { host.logThrowable(message, t); } } @Override public void logMessage(Level level, String message) { if (host != null) { host.logMessage(level, message); } } @Override public void runPlugin(String pluginName, String[] args, boolean runOnDedicatedThread, boolean suppressReturnedData) { host.runPlugin(pluginName, args, runOnDedicatedThread, suppressReturnedData); if (automaticallyClose) { //this.setVisible(false); this.dispose(); } } @Override public String[] getCurrentlyDisplayedFiles() { return host.getCurrentlyDisplayedFiles(); } @Override public String getHelpDirectory() { return host.getHelpDirectory(); } }