package jadex.application.space.envsupport.observer.gui;
import jadex.application.space.envsupport.dataview.IDataView;
import jadex.application.space.envsupport.environment.IEnvironmentSpace;
import jadex.application.space.envsupport.environment.space2d.Space2D;
import jadex.application.space.envsupport.math.IVector2;
import jadex.application.space.envsupport.observer.gui.plugin.IObserverCenterPlugin;
import jadex.application.space.envsupport.observer.gui.plugin.IntrospectorPlugin;
import jadex.application.space.envsupport.observer.gui.plugin.VisualsPlugin;
import jadex.application.space.envsupport.observer.perspective.IPerspective;
import jadex.application.space.envsupport.observer.perspective.Perspective2D;
import jadex.bridge.IComponentManagementService;
import jadex.commons.IChangeListener;
import jadex.commons.concurrent.SwingDefaultResultListener;
import jadex.commons.service.SServiceProvider;
import jadex.commons.service.clock.IClockService;
import jadex.commons.service.library.ILibraryService;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowStateListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* The default observer center.
*/
public class ObserverCenter
{
private static final int[] PLUGIN_REFRESH_TIMES = { 0, 50, 100, 250, 500, 1000};
private static final int[] VIEWPORT_RATES = { -1, 0, 5, 10, 20, 30, 50, 60, 70, 75, 85, 100, 120 };
private static final int DEFAULT_VIEWPORT_RATE = -1;
/** The main window. */
private ObserverCenterWindow mainwindow;
/** Currently active plugin. */
private IObserverCenterPlugin activeplugin;
/** The plugins. */
private IObserverCenterPlugin[] plugins;
/** Viewport refresh timer. */
private Timer vptimer;
/** Plugin refresh timer. */
private Timer plugintimer;
/** The library service */
private ILibraryService libservice;
/** Additional IDataView objects */
private Map externaldataviews;
/** Selected dataview name */
private String selecteddataviewname;
/** Perspectives */
private Map perspectives;
/** Selected Perspective */
private IPerspective selectedperspective;
/** The current space */
private Space2D space;
//TODO: move to Perspective!
/** Area size of the space */
// private IVector2 areasize;
/** Selected object listeners */
protected List selectedObjectListeners;
/** The clock listener for sync gui updates. */
protected IChangeListener clocklistener;
/** Kill the application on exit. */
protected boolean killonexit;
/** Creates an observer center.
*
* @param title title of the observer window
* @param space the space being observed
* @param libSrvc the platform library service for loading resources (images etc.)
* @param plugins custom plugins used in the observer
*/
public ObserverCenter(final String title, final IEnvironmentSpace space, ILibraryService libSrvc, List plugins, boolean killonexit)
{
selectedObjectListeners = Collections.synchronizedList(new ArrayList());
this.space = (Space2D) space;
this.killonexit = killonexit;
perspectives = Collections.synchronizedMap(new HashMap());
externaldataviews = Collections.synchronizedMap(new HashMap());
final List cplugins = plugins == null? new ArrayList(): plugins;
this.libservice = libSrvc;
Map spaceviews = space.getDataViews();
if(!spaceviews.isEmpty())
selecteddataviewname = (String)spaceviews.keySet().iterator().next();
activeplugin = null;
Runnable init = new Runnable()
{
public void run()
{
mainwindow = new ObserverCenterWindow(title);
loadPlugins(cplugins);
JMenu refreshMenu = new JMenu("Display");
JMenu pluginMenu = new JMenu("Plugin Refresh");
ButtonGroup group = new ButtonGroup();
for(int i = 0; i < PLUGIN_REFRESH_TIMES.length; ++i)
{
JRadioButtonMenuItem item = new JRadioButtonMenuItem(new PluginRefreshAction(PLUGIN_REFRESH_TIMES[i]));
if (PLUGIN_REFRESH_TIMES[i] == 100)
{
item.setSelected(true);
}
group.add(item);
pluginMenu.add(item);
}
refreshMenu.add(pluginMenu);
vptimer = new Timer(33, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
updateDisplay();
}
});
clocklistener = new IChangeListener()
{
public void changeOccurred(jadex.commons.ChangeEvent event)
{
updateDisplay();
}
};
JMenu viewportMenu = new JMenu("Viewport Refresh");
group = new ButtonGroup();
for(int i = 0; i < VIEWPORT_RATES.length; ++i)
{
JRadioButtonMenuItem item = new JRadioButtonMenuItem(new ViewportRefreshAction(VIEWPORT_RATES[i]));
if (VIEWPORT_RATES[i] == DEFAULT_VIEWPORT_RATE)
{
item.setSelected(true);
item.getAction().actionPerformed(null);
}
group.add(item);
viewportMenu.add(item);
}
refreshMenu.add(viewportMenu);
mainwindow.addMenu(refreshMenu);
plugintimer = new Timer(100, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
synchronized(ObserverCenter.this.plugins)
{
if(activeplugin != null)
{
activeplugin.refresh();
}
}
}
});
plugintimer.start();
mainwindow.addWindowListener(new ObserverWindowController());
mainwindow.addWindowStateListener(new WindowStateListener()
{
public void windowStateChanged(WindowEvent e)
{
IPerspective p = getSelectedPerspective();
if (p instanceof Perspective2D)
{
((Perspective2D) p).getViewport().refreshCanvasSize();
}
}
});
}
};
if(EventQueue.isDispatchThread())
{
init.run();
}
else
{
// try
// {
// EventQueue.invokeAndWait(init);
EventQueue.invokeLater(init);
// }
// catch (InterruptedException e)
// {
// }
// catch (InvocationTargetException e)
// {
// }
}
}
//TODO:Move to Perspective!
/**
* Returns the area size.
*
* @return area size
*/
public IVector2 getAreaSize()
{
return ((Space2D)space).getAreaSize().copy();
}
/**
* Adds an additional dataview.
* @param name name of the dataview
* @param dataview an additional dataview
*/
public void addDataView(String name, IDataView dataview)
{
synchronized(externaldataviews)
{
externaldataviews.put(name, dataview);
if (selecteddataviewname == null)
{
selecteddataviewname = name;
}
}
}
/**
* Returns the available dataviews.
*
* @return the available dataviews
*/
public Map getDataViews()
{
Map allViews = new HashMap();
allViews.putAll(externaldataviews);
allViews.putAll(space.getDataViews());
return allViews;
}
/**
* Returns the selected dataview.
*
* @return the selected dataview
*/
public IDataView getSelectedDataView()
{
IDataView dataview = space.getDataView(selecteddataviewname);
if(dataview == null)
dataview = (IDataView)externaldataviews.get(selecteddataviewname);
return dataview;
}
/**
* Returns the selected dataview name.
*
* @return the selected dataview name
*/
public String getSelectedDataViewName()
{
return selecteddataviewname;
}
/**
* Sets the selected dataview.
*
* @param name name of the dataview to be selected
*/
public void setSelectedDataView(String name)
{
selecteddataviewname = name;
selectedperspective.setSelectedObject(null);
}
/**
* Adds a perspective.
* @param name name of the perspective
* @param perspective the perspective
*/
public void addPerspective(final String name, final IPerspective perspective)
{
if(SwingUtilities.isEventDispatchThread())
{
synchronized(perspectives)
{
perspective.setObserverCenter(this);
perspective.setName(name);
perspectives.put(name, perspective);
if(perspectives.size() == 1)
{
setSelectedPerspective(name);
}
}
}
else
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
synchronized(perspectives)
{
perspective.setObserverCenter(ObserverCenter.this);
perspective.setName(name);
perspectives.put(name, perspective);
if (perspectives.size() == 1)
{
setSelectedPerspective(name);
}
}
}
});
}
}
/**
* Returns access to the library service
*
* @return the library service
*/
public ILibraryService getLibraryService()
{
return libservice;
}
/**
* Returns the available perspectives.
*
* @return the available perspectives
*/
public Map getPerspectives()
{
return perspectives;
}
/**
* Returns the selected perspective.
*
* @return the selected perspective
*/
public IPerspective getSelectedPerspective()
{
return selectedperspective;
}
/**
* Sets the selected perspective.
*
* @param name name of the perspective
*/
public void setSelectedPerspective(final String name)
{
if(SwingUtilities.isEventDispatchThread())
{
synchronized(perspectives)
{
IPerspective perspective = (IPerspective)perspectives.get(name);
perspective.setObserverCenter(this);
selectedperspective = perspective;
mainwindow.setPerspectiveView(perspective.getView());
perspective.setSelectedObject(null);
}
}
else
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
synchronized(perspectives)
{
IPerspective perspective = (IPerspective)perspectives.get(name);
perspective.setObserverCenter(ObserverCenter.this);
selectedperspective = perspective;
mainwindow.setPerspectiveView(perspective.getView());
perspective.setSelectedObject(null);
}
}
});
}
}
/**
* Sets the OpenGL mode for a perspective
* @param name name of the perspective
* @param opengl true to activate OpenGL mode
*/
public void setOpenGLMode(final String name, final boolean opengl)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
synchronized(perspectives)
{
IPerspective perspective = (IPerspective)perspectives.get(name);
double z = 1.0;
IVector2 ps = null;
if (perspective instanceof Perspective2D)
{
Perspective2D p = (Perspective2D) perspective;
z = p.getZoom();
ps = p.getViewport().getPosition();
}
perspective.setOpenGl(opengl);
if (name.equals(selectedperspective.getName()))
{
mainwindow.setPerspectiveView(selectedperspective.getView());
selectedperspective.reset();
}
if (perspective instanceof Perspective2D)
{
final Perspective2D p = (Perspective2D) perspective;
final IVector2 pos = ps;
final double zoom = z;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
p.setZoom(zoom);
p.getViewport().setPosition(pos);
}
});
}
}
}
});
}
/**
* Returns the space.
* @return the space
*/
public Space2D getSpace()
{
return space;
}
/**
* Adds a listener for change of the selected object
* @param object listener
*/
public void addSelectedObjectListener(ChangeListener listener)
{
selectedObjectListeners.add(listener);
}
/**
* Removes a listener for change of the selected object
* @param object listener
*/
public void removeSelectedObjectListener(ChangeListener listener)
{
selectedObjectListeners.remove(listener);
}
/**
* Fires a selected object change event.
*/
public void fireSelectedObjectChange()
{
synchronized(selectedObjectListeners)
{
for (Iterator it = selectedObjectListeners.iterator(); it.hasNext(); )
{
ChangeListener l = (ChangeListener) it.next();
l.stateChanged(new ChangeEvent(this));
}
}
}
/**
* Loads all available plugins
* @param customplugins custom plugins used in addition to standard plugins
*/
private void loadPlugins(List customplugins)
{
ArrayList plugins = new ArrayList();
IObserverCenterPlugin plugin = new IntrospectorPlugin();
// default plugins
// TODO: remove hard coding
plugins.add(plugin);
// TODO: port from simsupport
plugin = new VisualsPlugin();
plugins.add(plugin);
// plugin = new EvaluationPlugin();
// plugins.add(plugin);
//plugin = new ToolboxPlugin();
//plugins.add(plugin);
plugins.addAll(customplugins);
this.plugins = (IObserverCenterPlugin[])plugins.toArray(new IObserverCenterPlugin[0]);
for (int i = 0; i < this.plugins.length; ++i)
{
addPluginButton(this.plugins[i]);
}
if(this.plugins.length>0)
activatePlugin(this.plugins[0]);
}
/** Adds a plugin to the toolbar.
*
* @param plugin the plugin
*/
private void addPluginButton(IObserverCenterPlugin plugin)
{
String iconPath = plugin.getIconPath();
if (iconPath == null)
{
mainwindow.addToolbarItem(plugin.getName(), new PluginAction(plugin));
}
else
{
ClassLoader cl = libservice.getClassLoader();
try
{
// System.out.println(iconPath);
// System.out.println(cl.getResource(iconPath));
BufferedImage image = ImageIO.read(cl.getResource(iconPath));
ImageIcon icon = new ImageIcon(image);
mainwindow.addToolbarItem(plugin.getName(), icon, new PluginAction(plugin));
}
catch (Exception e)
{
System.err.println("Icon image " + iconPath + " not found.");
mainwindow.addToolbarItem(plugin.getName(), new PluginAction(plugin));
}
}
}
private void activatePlugin(IObserverCenterPlugin plugin)
{
synchronized(this.plugins)
{
IObserverCenterPlugin oldPlugin = activeplugin;
if (oldPlugin != null)
{
oldPlugin.shutdown();
}
mainwindow.setPluginView(plugin.getName(), plugin.getView());
plugin.start(this);
activeplugin = plugin;
}
}
/**
* Updates the display.
*/
private void updateDisplay()
{
synchronized(perspectives)
{
if(selectedperspective != null)
{
selectedperspective.refresh();
}
}
}
private class PluginAction extends AbstractAction
{
private IObserverCenterPlugin plugin;
public PluginAction(IObserverCenterPlugin plugin)
{
this.plugin = plugin;
}
public void actionPerformed(ActionEvent e)
{
activatePlugin(this.plugin);
}
}
private class PluginRefreshAction extends AbstractAction
{
private int time_;
/** Creates new PluginRefreshAction
*
* @param time Time delay to set
*/
public PluginRefreshAction(int time)
{
time_ = time;
if (time_ <= 0)
{
putValue(NAME, "Never");
}
else
{
putValue(NAME, Integer.toString(time_) + "ms");
}
}
public void actionPerformed(ActionEvent e)
{
plugintimer.stop();
if (time_ > 0)
{
plugintimer.setDelay(time_);
plugintimer.start();
}
}
}
private class ViewportRefreshAction extends AbstractAction
{
private int delay;
/** Creates new PluginRefreshAction
*
* @param fps frames per second
*/
public ViewportRefreshAction(int fps)
{
if(fps==-1)
{
delay = -1;
putValue(NAME, "Sync on Clock");
}
else if(fps==0)
{
delay = 0;
putValue(NAME, "Off");
}
else
{
delay = 1000 / fps;
putValue(NAME, Integer.toString(fps) + " FPS");
}
}
public void actionPerformed(ActionEvent e)
{
if(delay==-1)
{
SServiceProvider.getService(space.getContext().getServiceProvider(), IClockService.class)
.addResultListener(new SwingDefaultResultListener(mainwindow)
{
public void customResultAvailable(Object source, Object result)
{
vptimer.stop();
IClockService clock = (IClockService)result;
clock.addChangeListener(clocklistener);
}
});
}
else if(delay==0)
{
SServiceProvider.getService(space.getContext().getServiceProvider(), IClockService.class)
.addResultListener(new SwingDefaultResultListener(mainwindow)
{
public void customResultAvailable(Object source, Object result)
{
vptimer.stop();
IClockService clock = (IClockService)result;
clock.removeChangeListener(clocklistener);
}
});
}
else
{
SServiceProvider.getService(space.getContext().getServiceProvider(), IClockService.class)
.addResultListener(new SwingDefaultResultListener(mainwindow)
{
public void customResultAvailable(Object source, Object result)
{
IClockService clock = (IClockService)result;
clock.removeChangeListener(clocklistener);
vptimer.setDelay(delay);
vptimer.start();
}
});
}
}
}
private class ObserverWindowController implements WindowListener
{
public void windowActivated(WindowEvent e)
{
}
public void windowClosed(WindowEvent e)
{
}
public void windowClosing(WindowEvent e)
{
dispose();
if(killonexit)
{
SServiceProvider.getServiceUpwards(space.getContext().getServiceProvider(), IComponentManagementService.class)
.addResultListener(new SwingDefaultResultListener(mainwindow)
{
public void customResultAvailable(Object source, Object result)
{
IComponentManagementService cms = (IComponentManagementService)result;
cms.destroyComponent(space.getContext().getComponentIdentifier());
}
});
}
}
public void windowDeactivated(WindowEvent e)
{
}
public void windowDeiconified(WindowEvent e)
{
}
public void windowIconified(WindowEvent e)
{
}
public void windowOpened(WindowEvent e)
{
}
}
/**
* Dispose the observer center.
*/
public void dispose()
{
plugintimer.stop();
for (Iterator it = perspectives.values().iterator(); it.hasNext(); )
{
IPerspective persp = (IPerspective) it.next();
if (persp instanceof Perspective2D)
((Perspective2D) persp).getViewport().dispose();
}
if(clocklistener != null)
{
SServiceProvider.getService(space.getContext().getServiceProvider(), IClockService.class)
.addResultListener(new SwingDefaultResultListener(mainwindow)
{
public void customResultAvailable(Object source, Object result)
{
IClockService clock = (IClockService)result;
clock.removeChangeListener(clocklistener);
}
});
}
if (vptimer != null)
vptimer.stop();
if (plugintimer != null)
plugintimer.stop();
mainwindow.dispose();
}
}