/*
* Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved.
*
* This file is part of BoofCV (http://boofcv.org).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package boofcv.gui;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.awt.event.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
/**
* Application which lists most of the demonstration application in a GUI and allows the user to double click
* to launch one in a new JVM.
*
* @author Peter Abeles
*/
public abstract class ApplicationLauncherApp extends JPanel implements ActionListener {
private JTree tree;
JButton bKill = new JButton("Kill");
JButton bKillAll = new JButton("Kill All");
JList processList;
DefaultListModel listModel = new DefaultListModel();
int memoryMB = 1024;
final List<ActiveProcess> processes = new ArrayList<>();
public ApplicationLauncherApp() {
setLayout(new BorderLayout());
DefaultMutableTreeNode root = new DefaultMutableTreeNode("All Categories");
createTree(root);
tree = new JTree(root);
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
// tree.addTreeSelectionListener(this);
MouseListener ml = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int selRow = tree.getRowForLocation(e.getX(), e.getY());
TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
if(e.getClickCount() == 2) {
handleClick((DefaultMutableTreeNode)tree.getLastSelectedPathComponent());
}
}
};
tree.addMouseListener(ml);
JScrollPane treeView = new JScrollPane(tree);
treeView.setPreferredSize(new Dimension(300,600));
JPanel actionPanel = new JPanel();
actionPanel.setLayout(new BoxLayout(actionPanel, BoxLayout.X_AXIS));
actionPanel.add(bKill);
actionPanel.add(Box.createHorizontalGlue());
actionPanel.add(bKillAll);
bKill.addActionListener(this);
bKillAll.addActionListener(this);
processList = new JList(listModel);
processList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
processList.setLayoutOrientation(JList.VERTICAL);
processList.setVisibleRowCount(-1);
processList.setPreferredSize(new Dimension(500,600));
JPanel processPanel = new JPanel();
processPanel.setLayout(new BoxLayout(processPanel, BoxLayout.Y_AXIS));
processPanel.add( actionPanel );
processPanel.add( processList );
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitPane.add(treeView);
splitPane.add(processPanel);
add( splitPane, BorderLayout.CENTER );
// setPreferredSize(new Dimension(400,600));
new ProcessStatusThread().start();
}
protected abstract void createTree( DefaultMutableTreeNode root );
protected void createNodes( DefaultMutableTreeNode root, String subjectName, Class ...apps) {
DefaultMutableTreeNode top = new DefaultMutableTreeNode(subjectName);
for (int i = 0; i < apps.length; i++) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(new AppInfo(apps[i]));
top.add(node);
}
root.add(top);
}
private void launch( AppInfo info ) {
List<String> classPath = new ArrayList<>();
ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader)cl).getURLs();
for(URL url: urls){
classPath.add( url.getFile());
}
final ActiveProcess process = new ActiveProcess();
process.info = info;
process.launcher = new JavaRuntimeLauncher(classPath);
process.launcher.setFrozenTime(-1);
process.launcher.setMemoryInMB(memoryMB);
synchronized (processes) {
processes.add(process);
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
listModel.addElement(process);
processList.invalidate();
}
});
process.start();
}
public void handleClick( DefaultMutableTreeNode node ) {
if (node == null)
return;
if (!node.isLeaf())
return;
AppInfo info = (AppInfo)node.getUserObject();
System.out.println("clicked "+info);
launch(info);
}
@Override
public void actionPerformed(ActionEvent e) {
if( e.getSource() == bKill ) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
ActiveProcess selected = (ActiveProcess)processList.getSelectedValue();
if( selected == null )
return;
selected.kill();
}
});
} else if( e.getSource() == bKillAll ) {
synchronized (processes ) {
for (int i = 0; i < processes.size(); i++) {
processes.get(i).kill();
}
}
}
}
public static class AppInfo {
Class app;
public AppInfo(Class app) {
this.app = app;
}
@Override
public String toString() {
return app.getSimpleName();
}
}
public static class ActiveProcess extends Thread {
AppInfo info;
JavaRuntimeLauncher launcher;
volatile boolean active = false;
JavaRuntimeLauncher.Exit exit;
@Override
public void run() {
active = true;
exit = launcher.launch(info.app);
System.out.println();
System.out.println("------------------- Exit condition "+exit);
active = false;
}
public void kill() {
launcher.requestKill();
}
public boolean isActive() {
return active;
}
@Override
public String toString() {
if( launcher.isKillRequested() && active ) {
return "Killing "+info;
} else {
return info.toString();
}
}
}
class ProcessStatusThread extends Thread {
@Override
public void run() {
while( true ) {
synchronized (processes) {
for (int i = processes.size()-1; i >= 0; i--) {
final ActiveProcess p = processes.get(i);
if( !p.isActive() ) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
listModel.removeElement(p);
processList.invalidate();
}
});
processes.remove(i);
}
}
}
try {
sleep(250);
} catch (InterruptedException e) {
break;
}
}
}
}
}