/*******************************************************************************
* Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser Public License v3
* which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt
******************************************************************************/
package com.opendoorlogistics.studio.scripts.list;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Action;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import org.apache.commons.io.FilenameUtils;
import com.opendoorlogistics.api.ODLApi;
import com.opendoorlogistics.api.ui.Disposable;
import com.opendoorlogistics.core.scripts.ScriptConstants;
import com.opendoorlogistics.core.scripts.ScriptsProvider;
import com.opendoorlogistics.core.scripts.ScriptsProvider.HasScriptsProvider;
import com.opendoorlogistics.core.scripts.elements.Script;
import com.opendoorlogistics.core.scripts.elements.ScriptEditorType;
import com.opendoorlogistics.core.utils.io.WatchSingleDirectory;
import com.opendoorlogistics.core.utils.io.WatchSingleDirectory.DirectoryChangedListener;
import com.opendoorlogistics.core.utils.ui.FileBrowserPanel;
import com.opendoorlogistics.api.ui.UIFactory.FilenameChangeListener;
import com.opendoorlogistics.core.utils.ui.LayoutUtils;
import com.opendoorlogistics.studio.PreferencesManager;
import com.opendoorlogistics.studio.PreferencesManager.PrefKey;
import com.opendoorlogistics.studio.appframe.AppPermissions;
import com.opendoorlogistics.studio.scripts.componentwizard.SetupComponentWizard;
import com.opendoorlogistics.studio.scripts.execution.ScriptUIManager;
import com.opendoorlogistics.studio.utils.SwingFriendlyDirectoryChangedListener;
import com.opendoorlogistics.utils.ui.SimpleAction;
import com.opendoorlogistics.utils.ui.SimpleActionConfig;
final public class ScriptsPanel extends JPanel implements DirectoryChangedListener, Disposable, HasScriptsProvider {
private final ScriptsTree scriptsTree;
private final List<MyAction> actions;
private final ScriptUIManager scriptUIManager;
private final JPopupMenu popup;
private final ODLApi api;
private final FileBrowserPanel dirChooser;
private File directory;
private WatchSingleDirectory watcher;
// public boolean isRunnable(ScriptNode node) {
// return node != null && launchScriptEditor.hasLoadedData() && node.isAvailable() && node.isRunnable();
// }
private abstract class MyAction extends SimpleAction {
private final boolean needsLoadedData;
private final boolean needsRunnable;
private final boolean needsAvailable;
public MyAction(SimpleActionConfig config, boolean needsLoadedData, boolean needsRunnable, boolean needsAvailable) {
super(config);
this.needsLoadedData = needsLoadedData;
this.needsRunnable = needsRunnable;
this.needsAvailable = needsAvailable;
}
@Override
public void updateEnabledState() {
ScriptNode selected = scriptsTree.getSelectedValue();
boolean enabled = true;
if (requiresSelection && selected == null) {
enabled = false;
}
if (enabled && needsLoadedData && scriptUIManager.hasLoadedData() == false) {
enabled = false;
}
if (enabled && needsRunnable && (selected == null || selected.isRunnable() == false)) {
enabled = false;
}
if (enabled && needsAvailable && (selected == null || selected.isAvailable() == false)) {
enabled = false;
}
setEnabled(enabled);
}
}
/**
* Create the panel.
*/
public ScriptsPanel(ODLApi api, File directory, ScriptUIManager launchScriptEditor) {
this.scriptUIManager = launchScriptEditor;
this.api = api;
// find a sensible directory
if (directory == null) {
directory = new File(ScriptConstants.DIRECTORY);
if (!directory.exists()) {
directory = new File("");
}
}
this.directory = directory;
setLayout(new BorderLayout(0, 0));
// Add directory browser and label at the top in their own panel.
// Label is wrapped in a panel because alignment is being ignored and this at least makes it properly centred.
// See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4275005
boolean lockedDir = scriptUIManager.getAppPermissions().isScriptDirectoryLocked();
if (!lockedDir) {
JLabel lblLabel = new JLabel("Scripts directory");
JPanel labelPanel = new JPanel(new BorderLayout());
labelPanel.add(lblLabel, BorderLayout.CENTER);
labelPanel.setMaximumSize(lblLabel.getMinimumSize());
dirChooser = new FileBrowserPanel(directory.getAbsolutePath(), new FilenameChangeListener() {
@Override
public void filenameChanged(String newFilename) {
ScriptsPanel.this.directory = new File(newFilename);
onDirectoryChanged(ScriptsPanel.this.directory);
}
}, true, "Select");
JPanel topPanel = LayoutUtils.createVerticalBoxLayout(labelPanel, dirChooser);
add(topPanel, BorderLayout.NORTH);
} else {
dirChooser = null;
}
// add toolbar at the bottom
JToolBar toolBar = new JToolBar();
toolBar.setFloatable(false);
add(toolBar, BorderLayout.SOUTH);
// create all actions and add as buttons and menu items
popup = new JPopupMenu();
actions = createActions(launchScriptEditor.getAppPermissions());
for (Action action : actions) {
toolBar.add(action);
popup.add(action);
}
// add list in the centre
scriptsTree = new ScriptsTree(scriptUIManager, popup);
scriptsTree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
ScriptsPanel.this.updateAppearance();
}
});
add(scriptsTree.getScrollPane(), BorderLayout.CENTER);
// // create selection changed listener on the list
// listControl.addListSelectionListener(new ListSelectionListener() {
//
// @Override
// public void valueChanged(ListSelectionEvent e) {
// updateAppearance();
// }
// });
// finally file the list
onDirectoryChanged(directory);
}
public List<File> getScriptsByType(ScriptEditorType type) {
return scriptsTree.getScriptsByType(type);
}
private boolean isOkDirectory() {
return directory != null && directory.isDirectory() && directory.exists();
}
private List<MyAction> createActions(AppPermissions appPermissions) {
ArrayList<MyAction> ret = new ArrayList<>();
if (appPermissions.isScriptEditingAllowed()) {
ret.add(new MyAction(SimpleActionConfig.addItem.setItemName("script"), false, false, false) {
@Override
public void actionPerformed(ActionEvent e) {
Script script = new SetupComponentWizard(SwingUtilities.getWindowAncestor(ScriptsPanel.this), api, scriptUIManager
.getAvailableFieldsQuery()).showModal();
// Script script =new ScriptWizardActions(api,SwingUtilities.getWindowAncestor(ScriptsPanel.this)).promptUser();
if (script != null) {
scriptUIManager.launchScriptEditor(script, null, isOkDirectory() ? directory : null);
}
}
});
ret.add(new MyAction(SimpleActionConfig.editItem.setItemName("script"), false, false, true) {
@Override
public void actionPerformed(ActionEvent e) {
ScriptNode node = scriptsTree.getSelectedValue();
if (node != null && node.isAvailable()) {
ScriptsPanel.this.scriptUIManager.launchScriptEditor(node.getFile(), node.getLaunchEditorId());
}
}
});
ret.add(new MyAction(SimpleActionConfig.deleteItem.setItemName("script"), false, false, false) {
@Override
public void actionPerformed(ActionEvent e) {
ScriptNode node = scriptsTree.getSelectedValue();
if (node == null) {
return;
}
if (JOptionPane.showConfirmDialog(ScriptsPanel.this, "Really delete script " + node.getFile().getName() + " from disk?", "Delete script",
JOptionPane.YES_NO_OPTION) == JOptionPane.OK_OPTION) {
if (!node.getFile().delete()) {
JOptionPane.showMessageDialog(ScriptsPanel.this, "Could not delete file");
} else {
onDirectoryChanged(directory);
}
}
}
@Override
public void updateEnabledState() {
ScriptNode selected = scriptsTree.getSelectedValue();
boolean enabled = true;
if (selected == null) {
enabled = false;
}
if (enabled && selected.isScriptRoot() == false) {
// can only delete the root
enabled = false;
}
setEnabled(enabled);
}
});
ret.add(new MyAction(SimpleActionConfig.testCompileScript, true, false, true) {
@Override
public void actionPerformed(ActionEvent e) {
ScriptNode node = scriptsTree.getSelectedValue();
if (node != null) {
scriptUIManager.testCompileScript(node.getFile(), node.getLaunchExecutorId());
}
}
});
ret.add(new MyAction(SimpleActionConfig.runScript, true, true, true) {
@Override
public void actionPerformed(ActionEvent e) {
ScriptNode node = scriptsTree.getSelectedValue();
if (node != null) {
scriptUIManager.executeScript(node.getFile(), node.getLaunchExecutorId());
}
}
@Override
public void updateEnabledState() {
setEnabled(ScriptNode.isRunnable(scriptsTree.getSelectedValue(), scriptUIManager));
}
});
}
return ret;
}
public void updateAppearance() {
for (MyAction action : actions) {
action.updateEnabledState();
}
scriptsTree.updateAppearance();
}
@Override
public void onDirectoryChanged(File directory) {
// set new directory
this.directory = directory;
PreferencesManager.getSingleton().setDirectory(PrefKey.SCRIPTS_DIR, directory);
// check to see if watcher is pointing at wrong one
if (watcher != null && watcher.getDirectory().equals(directory) == false) {
shutdownDirectoryWatcher();
}
ScriptNode selected = scriptsTree.getSelectedValue();
if (isOkDirectory()) {
scriptsTree.setEnabled(true);
final File[] files = directory.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isFile() && FilenameUtils.isExtension(pathname.getAbsolutePath(), ScriptConstants.FILE_EXT);
}
});
scriptsTree.setFiles(files);
// reselect
if (selected != null) {
scriptsTree.setSelected(selected);
}
// init watcher
if (watcher == null) {
watcher = WatchSingleDirectory.launch(directory, new SwingFriendlyDirectoryChangedListener(this));
}
} else {
shutdownDirectoryWatcher();
// set list to empty
scriptsTree.setFiles(new File[] {});
scriptsTree.setEnabled(false);
}
updateAppearance();
}
public void shutdownDirectoryWatcher() {
if (watcher != null) {
watcher.shutdown();
watcher = null;
}
}
// public static void main(String[] args) {
//
// javax.swing.SwingUtilities.invokeLater(new Runnable() {
// public void run() {
// InitialiseStudio.initialise();
// JFrame frame = new JFrame("");
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// // frame.setContentPane(new ScriptsPanel(new File("C:\\Users\\Phil\\Documents")));
// frame.setContentPane(new ScriptsPanelV2(null, null));
// frame.pack();
// frame.setSize(600, 600);
// frame.setVisible(true);
// }
// });
// }
@Override
public synchronized void dispose() {
if (watcher != null) {
watcher.shutdown();
watcher = null;
}
}
public ScriptNode[] getScripts() {
return scriptsTree.getScriptNodes();
}
@Override
public ScriptsProvider getScriptsProvider() {
return scriptsTree.getScriptsProvider();
}
public void setScriptsDirectory(File directory) {
if (dirChooser != null) {
dirChooser.setFilename(directory.getAbsolutePath());
}
onDirectoryChanged(directory);
}
}