package org.freehep.application;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Frame;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.print.Pageable;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.swing.AbstractButton;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.xml.parsers.SAXParserFactory;
import org.freehep.application.PropertyUtilities.TranslatedProperties;
import org.freehep.application.services.ServiceManager;
import org.freehep.swing.ErrorDialog;
import org.freehep.swing.RecentFileList;
import org.freehep.swing.popup.GlobalMouseListener;
import org.freehep.swing.popup.GlobalPopupListener;
import org.freehep.swing.popup.PopupListener;
import org.freehep.util.VersionComparator;
import org.freehep.util.commanddispatcher.BooleanCommandState;
import org.freehep.util.commanddispatcher.CommandProcessor;
import org.freehep.util.commanddispatcher.CommandSourceAdapter;
import org.freehep.util.commanddispatcher.CommandTarget;
import org.freehep.util.commanddispatcher.CommandTargetManager;
import org.freehep.util.commandline.CommandLine;
import org.freehep.util.commandline.CommandLineException;
import org.freehep.xml.menus.XMLMenuBuilder;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
// import kitchen.sink.*;
/**
* A framework for a simple swing application. This framework assumes there will be
* only one top level window associated with the application, which can always be accessed
* using the static method Application.getApplication();
* <p>
* The framework provides the following facilities:
* <ul>
* <li>Access to a ServiceManager to make it easy for an application to run in
* the Java Web Start Environment as well as as a standalone application. Support for Java
* Applets (using the Java Plugin) could be added as well in future.
* <li>Print Preview capability
* <li>Recent file menu
* <li>A simple method of reporting error messages to the user.
* <li>Access to help files, using JavaHelp
* <li>Use of XML to define menus and toolbars
* <li>Use of a command manager for dispatching commands to command targets.
* <li>Support for user preferences to be stored between invocations.
* </ul>
* The easiest way to use the framework is to provide an application properties file.
* The following properies are supported:
* <dl>
* <dt>mainClass</dt><dd>The main class (which must extend Application)</dd>
* <dt>title</dt><dd>The initial title for the main frame</dd>
* <dt>menus</dt><dd>The name of an XML file to load which defines menus</dd>
* </dl>
*
* The framework basically ties together a number of features that are implemented
* elsewhere in the FreeHEP library. By using this framework you are able to easily
* use some or all of these features. The framework has been implemented in such a
* way as to ensure there is almost no overhead paid for features that are not used.
* @author tonyj
* @version $Id: Application.java 13767 2010-07-22 15:39:17Z tonyj $
*/
public class Application extends JPanel
{
/** Creates new Application */
protected Application(String appName)
{
super(new BorderLayout());
if (theApp != null) throw new RuntimeException("Attempt to create multiple instances of Application");
this.appName = appName;
theApp = this;
// Check the JVM version ASAP
try
{
String actual = System.getProperty("java.version");
String required = getAppProperties().getProperty("minimumJavaVersion");
if (versionNumberCompare(actual,required)<0) throw new RuntimeException("Wrong Java version required: "+required+" actual: "+actual);
}
catch (SecurityException x)
{}
catch (NumberFormatException x)
{}
}
/**
* Override this method to provide a specialized ServiceManager.
* By default this method checks the System property org.freehep.application.ServiceManager, and uses its value
* as the name of the class to instantiate. If the property is undefined we use
* org.freehep.application.services.app.AppServiceManager
* @see org.freehep.application.services.app.AppServiceManager
*/
protected ServiceManager createServiceManager() throws InitializationException
{
String className = System.getProperty("jnlp.org.freehep.application.ServiceManager");
if (className == null) className = System.getProperty("org.freehep.application.ServiceManager");
if (className == null) className = "org.freehep.application.services.app.AppServiceManager";
return (ServiceManager) createObjectFromProperty(className,ServiceManager.class);
}
public ServiceManager getServiceManager()
{
if (serviceManager == null) serviceManager = createServiceManager();
return serviceManager;
}
public static Application getApplication()
{
return theApp;
}
public RecentFileList getRecentFileList(String name)
{
if (rflHash == null) rflHash = new Hashtable();
RecentFileList rfl = (RecentFileList) rflHash.get(name);
if (rfl == null)
{
rfl = new RecentFileList(name);
rfl.load(getUserProperties());
rflHash.put(name,rfl);
}
return rfl;
}
protected void saveUserProperties()
{
Properties userProperties = getUserProperties();
// Save any recent file lists
if (rflHash != null)
{
Enumeration e = rflHash.elements();
while (e.hasMoreElements())
{
RecentFileList rfl = (RecentFileList) e.nextElement();
rfl.save(userProperties);
}
}
// Save the window size/position
JFrame f = (JFrame) SwingUtilities.getAncestorOfClass(JFrame.class,this);
if (f != null && f.getState() == Frame.NORMAL) PropertyUtilities.setRectangle(userProperties,"window",f.getBounds());
storeUserProperties(getUserProperties());
}
private void loadUserProperties(Properties user)
{
getServiceManager().loadUserPreferences(user);
}
private void storeUserProperties(Properties user)
{
getServiceManager().storeUserPreferences(user);
}
public void setLookAndFeel(String lookAndFeelName)
{
try
{
if (!UIManager.getLookAndFeel().getClass().getName().equals(lookAndFeelName))
{
UIManager.setLookAndFeel(lookAndFeelName);
Frame f = (Frame) SwingUtilities.getAncestorOfClass(Frame.class,this);
if (f != null) updateComponentTreeUI(f);
}
getUserProperties().setProperty("lookAndFeel",lookAndFeelName);
}
catch (Exception x)
{
error("Could not set look and feel "+lookAndFeelName,x);
}
}
public String getVersion()
{
return getAppProperties().getProperty("version");
}
public String getFullVersion()
{
Properties prop = getAppProperties();
return prop.getProperty("fullVersion");
}
/**
* Compares two version numbers of the form 1.2.3.4
* @return >0 if v1>v2, <0 if v1<v2 or 0 if v1=v2
*/
public static int versionNumberCompare(String v1, String v2) throws NumberFormatException
{
VersionComparator comp = new VersionComparator();
return comp.versionNumberCompare(v1,v2);
}
public PrintPreview createPrintPreview()
{
return new AppPrintPreview();
}
/**
* Shows a dialog and remembers its screen position (across sessions)
*/
public void showDialog(JDialog dlg, String key)
{
Rectangle bounds = PropertyUtilities.getRectangle(getUserProperties(),key,null);
if (bounds == null)
{
dlg.pack();
dlg.setLocationRelativeTo(this);
}
else dlg.setBounds(bounds);
dlg.setVisible(true);
PropertyUtilities.setRectangle(getUserProperties(),key,dlg.getBounds());
}
public CommandProcessor getCommandProcessor()
{
if (commandProcessor == null) commandProcessor = createCommandProcessor();
return commandProcessor;
}
protected CommandProcessor createCommandProcessor()
{
return new ApplicationCommandProcessor();
}
public CommandTargetManager getCommandTargetManager()
{
if (commandTargetManager == null) commandTargetManager = createCommandTargetManager();
return commandTargetManager;
}
protected CommandLine createCommandLine()
{
Properties props = getAppProperties();
int n = PropertyUtilities.getInteger(props,"numberOfParameters");
boolean multiLevel = n < 0;
return new CommandLine(getAppName(),getFullVersion(),Math.max(n,0),multiLevel);
}
public CommandLine getCommandLine()
{
if (commandLine == null)
{
commandLine = createCommandLine();
// register standard options
commandLine.addOption("noSplash",null,"Suppress splash screen on startup");
commandLine.addOption("clearUserProperties",null,"Clears the user properties");
commandLine.addBailOutOption("help","h","Display this message");
commandLine.addMultiOption("D","key=value","Set application property");
commandLine.addMultiOption("d","key=value","Set user property, persists between sessions");
}
return commandLine;
}
protected CommandTargetManager createCommandTargetManager()
{
return new ApplicationCommandTargetManager();
}
public final void showHelpTopic(String helpTopicTarget)
{
showHelpTopic(helpTopicTarget,"TOC");
}
public void showHelpTopic(String helpTopicTarget, Component owner)
{
showHelpTopic(helpTopicTarget,"TOC",owner);
}
private void showHelpTopic(String helpTopicTarget, String navigatorView)
{
showHelpTopic(helpTopicTarget, navigatorView, this);
}
private void showHelpTopic(final String helpTopicTarget, final String navigatorView, final Component owner)
{
Runnable r2 = new Runnable()
{
public void run()
{
try
{
HelpService hs = getHelpService();
hs.showHelpTopic(helpTopicTarget, navigatorView, owner);
}
catch (IllegalArgumentException x)
{
error("Help topic not found",x);
}
}
};
whenAvailable("help",r2);
}
/**
* Run Runnable when part is downloaded and available.
* Runnable is always run on the event dispatch thread.
* @param part The part will be downloaded (if necessary)
* @param run The Runnable that will be run
*/
public void whenAvailable(final String part, final Runnable run)
{
try
{
if (!getServiceManager().isAvailable(part))
{
final Runnable r1 = new Runnable()
{
public void run()
{
if (getServiceManager().makeAvailable(part))
{
SwingUtilities.invokeLater(run);
}
}
};
Thread download = new Thread(r1);
download.start();
}
else
{
run.run();
}
}
catch (InitializationException x)
{
error("Could not download "+part,x);
}
}
private HelpService getHelpService() throws InitializationException
{
if (helpService == null) helpService = createHelpService();
return helpService;
}
protected HelpService createHelpService() throws InitializationException
{
try
{
return (HelpService) Class.forName("org.freehep.application.HelpServiceImpl").newInstance();
}
catch (ExceptionInInitializerError x)
{
Throwable t = x.getException();
throw new InitializationException("Could not create help service", t == null ? x : t);
}
catch (Throwable x)
{
throw new InitializationException("Could not create help service",x);
}
}
/**
* Shows the table of contents for the help system.
*/
public final void showHelpContents()
{
showHelpTopic(null);
}
/**
* Shows the index for the help system.
*/
public final void showHelpIndex()
{
showHelpTopic(null, "Index");
}
/**
* Opens a search window for the help system.
*/
public final void showHelpSearch()
{
showHelpTopic(null, "Search");
}
public void about()
{
JDialog dlg = createAboutDialog();
dlg.pack();
dlg.setLocationRelativeTo(this);
dlg.setVisible(true);
}
/**
* Show an error dialog with a simple message
* This method is thread safe and can be called from any thread.
* @param message The message to display
*/
public void error(String message)
{
error(message,null);
}
/**
* Show an error dialog with a message and supporting detail.
* This method is thread safe and can be called from any thread.
* @param message The error message
* @param detail the nested exception
*/
public void error(final String message, final Throwable detail)
{
synchronized (queueLock)
{
if (queueRunnable != null)
{
Runnable run = new Runnable()
{
public void run()
{
error(message,detail);
}
};
queueRunnable.add(run);
}
else error(this,message,detail);
}
}
/**
* Show an error message in an error dialog.
* This method is thread safe and can be called from any thread.
* @param source The owner of the error dialog
* @param message The error message
*/
public static void error(Component source, String message)
{
error(source,message,null);
}
/**
* Show an error message in an error dialog, with optional supporting detail.
* If the detail implements HasNestedException the nested exception(s) will also be shown.
* This method is thread safe and can be called from any thread.
* @param source The owner of the error dialog
* @param message The error message
* @param detail A throwable giving more details on the error.
* @see org.freehep.util.HasNestedException
*/
public static void error(final Component source, final String message, final Throwable detail)
{
if (!SwingUtilities.isEventDispatchThread())
{
Runnable run = new Runnable()
{
public void run()
{
error(source,message,detail);
}
};
SwingUtilities.invokeLater(run);
}
else
{
ErrorDialog.showErrorDialog(source, message, detail);
}
}
protected JDialog createAboutDialog()
{
return new AboutDialog(this);
}
protected JPanel createToolBarHolder()
{
return new JPanel(new BorderLayout());
}
public JPanel getToolBarHolder()
{
if (toolBarHolder == null)
{
toolBarHolder = createToolBarHolder();
if (toolBarVisible)
{
toolBarHolder.add(this);
menuPane.add(toolBarHolder);
}
}
return toolBarHolder;
}
/**
* Override this to provide an application specific status bar
*/
protected StatusBar createStatusBar()
{
return new StatusBar();
}
public StatusBar getStatusBar()
{
if (statusBar == null) statusBar = createStatusBar();
return statusBar;
}
/**
* Display a message in the applications status bar.
* This method is thread safe and can be called from any thread.
* @param message The message to display
*/
public void setStatusMessage(String message)
{
if (l != null) l.setStage(message);
else getStatusBar().setMessage(message);
}
public boolean getShowStatusBar()
{
return statusBarVisible;
}
/**
* Show or Hide the status bar. This setting presists between sessions.
* @param show true to display the status bar
*/
public void setShowStatusBar(boolean show)
{
if (show != statusBarVisible)
{
StatusBar sb = getStatusBar();
if (show)
{
menuPane.add(sb,BorderLayout.SOUTH);
}
else
{
menuPane.remove(sb);
}
statusBarVisible = show;
menuPane.revalidate();
PropertyUtilities.setBoolean(getUserProperties(),"showStatusBar",show);
}
}
public boolean getShowToolBar()
{
return toolBarVisible;
}
/**
* Show or Hide the tool bar. This setting presists between sessions.
* @param show true to display the tool bar
*/
public void setShowToolBar(boolean show)
{
if (show != toolBarVisible)
{
if (toolBarHolder != null)
{
if (show)
{
toolBarHolder.add(this);
menuPane.add(toolBarHolder);
}
else
{
menuPane.remove(toolBarHolder);
menuPane.add(this);
}
}
menuPane.revalidate();
menuPane.repaint();
toolBarVisible = show;
PropertyUtilities.setBoolean(getUserProperties(),"showToolBar",show);
}
}
/**
* Override this method to provide specialized application exit handling.
* For example to double check with the user before exiting. The default
* implementation saves the user preferences and then calls System.exit(0).
*/
public void exit()
{
String confirm = getUserProperties().getProperty("confirmQuit");
if (!confirm.equals("never"))
{
String confirmQuitMessage = getUserProperties().getProperty("confirmQuitMessage");
String confirmQuitTitle = getUserProperties().getProperty("confirmQuitTitle");
Box message = Box.createVerticalBox();
message.add(new JLabel(confirmQuitMessage));
JCheckBox ask = new JCheckBox("Don't ask me this again");
if (!confirm.equals("always")) message.add(ask);
if (JOptionPane.showConfirmDialog(this,message,confirmQuitTitle,JOptionPane.YES_NO_OPTION)!=JOptionPane.YES_OPTION) return;
if (ask.isSelected()) getUserProperties().setProperty("confirmQuit","never");
}
fireAboutToExit(new ApplicationEvent(this,ApplicationEvent.APPLICATION_EXITING));
saveUserProperties();
System.exit(0);
}
protected InitializationListener createSplashScreen()
{
final Properties prop = getUserProperties();
boolean noSplash = getCommandLine().getOption("noSplash") != null;
if (!noSplash && PropertyUtilities.getBoolean(prop,"showSplash",true))
{
String splashTitle = prop.getProperty("splashTitle");
URL url = PropertyUtilities.getURL(prop,"splashImage",null);
if (url == null) throw new InitializationException("Could not find splash image");
final SplashScreen splash = new SplashScreen(new ImageIcon(url),"Starting...",splashTitle);
final int nSteps = PropertyUtilities.getInteger(prop,"numberOfInitializationStages",10);
splash.setVisible(true);
return new InitializationListener()
{
private int n = 0;
public void setStage(String message)
{ splash.showStatus(message,100*n++/nSteps); }
public void failed(Throwable t)
{ splash.close(); }
public void succeeded()
{ splash.close(); PropertyUtilities.setInteger(prop,"numberOfInitializationStages",n); }
};
}
else return null;
}
/**
* Creates a JFrame that holds the application.
* If the application has been run before, and the user properties were
* saved, will attempt to restore the old window position/size.
*/
public JFrame createFrame(String[] argv) throws InitializationException
{
try
{
queueRunnable = new ArrayList();
CommandLine cl = getCommandLine();
boolean rc = cl.parse(argv);
if (!rc)
{
System.out.println(cl.getHelp());
System.exit(0);
}
// Deal with clearUserProperties
if (cl.hasOption("clearUserProperties")) getUserProperties().clear();
// Deal with explicitly set properties
Vector v = cl.getMultiOption("D");
Properties prop = getAppProperties();
for (Enumeration e = v.elements(); e.hasMoreElements(); )
{
String opt = e.nextElement().toString();
StringTokenizer s = new StringTokenizer(opt,"=");
int n = s.countTokens();
if (n == 1) prop.setProperty(s.nextToken(),"true");
else if (n == 2) prop.setProperty(s.nextToken(),s.nextToken());
else throw new InitializationException("Illegal value \""+opt+"\" for option");
}
// Alternative way to set app/user properties (compatible with javaws -open)
Properties properties = System.getProperties();
for (Iterator i = properties.keySet().iterator(); i.hasNext(); )
{
String key = i.next().toString();
if (key.startsWith("freehep.app."))
{
String value = properties.getProperty(key);
getAppProperties().setProperty(key.substring(12),value);
i.remove();
}
}
// Deal with explicitly set user properties
// Note it is important to deal with application properties before
// the first call to getUserProperties, since the applicaiton properties
// can affect where the user properties are loaded from.
v = cl.getMultiOption("d");
prop = getUserProperties();
for (Enumeration e = v.elements(); e.hasMoreElements(); )
{
String opt = e.nextElement().toString();
StringTokenizer s = new StringTokenizer(opt,"=");
int n = s.countTokens();
if (n == 1) prop.setProperty(s.nextToken(),"true");
else if (n == 2) prop.setProperty(s.nextToken(),s.nextToken());
else throw new InitializationException("Illegal value \""+opt+"\" for option");
}
for (Iterator i = properties.keySet().iterator(); i.hasNext(); )
{
String key = i.next().toString();
if (key.startsWith("freehep.user."))
{
String value = properties.getProperty(key);
getUserProperties().setProperty(key.substring(13),value);
i.remove();
}
}
}
catch (CommandLineException x)
{
throw new InitializationException("Illegal Command Line Argument",x);
}
InitializationListener splash = createSplashScreen();
if (splash != null) l = splash;
try
{
setStatusMessage("Creating command manager");
CommandTargetManager ctm = getCommandTargetManager();
ctm.add(getCommandProcessor());
setStatusMessage("Creating main frame");
Properties appProp = getUserProperties();
URL iconURL = PropertyUtilities.getURL(appProp,"icon",null);
Image iconImage = iconURL == null ? null : new ImageIcon(iconURL).getImage();
// TODO: Can we do this only when the menuPane is needed?
menuPane = new JPanel(new BorderLayout());
menuPane.add(this);
String toolbar = appProp.getProperty("topLevelToolBar");
if (toolbar != null)
{
JToolBar tb = getXMLMenuBuilder().getToolBar(toolbar);
if (tb == null) throw new InitializationException("Could not find toolbar "+toolbar);
getToolBarHolder().add(tb,BorderLayout.NORTH);
}
String topLevelMenu = appProp.getProperty("topLevelMenuBar");
if (topLevelMenu != null)
{
menuBar = getXMLMenuBuilder().getMenuBar(topLevelMenu);
if (menuBar == null) throw new InitializationException("Could not find menuBar "+topLevelMenu);
}
String topLevelPopupMenu = appProp.getProperty("topLevelPopupMenu");
if (topLevelPopupMenu != null)
{
JPopupMenu popup = getXMLMenuBuilder().getPopupMenu(topLevelPopupMenu);
if (popup == null) throw new InitializationException("Could not find popup menu "+topLevelPopupMenu);
this.addMouseListener(new PopupListener(popup));
}
boolean showToolBar = PropertyUtilities.getBoolean(appProp,"showToolBar",true);
setShowToolBar(showToolBar);
boolean showStatus = PropertyUtilities.getBoolean(appProp,"showStatusBar",true);
setShowStatusBar(showStatus);
init();
fireInitializationComplete(new ApplicationEvent(this,ApplicationEvent.INITIALIZATION_COMPLETE));
setStatusMessage("Setting Look and Feel...");
String laf = getUserProperties().getProperty("lookAndFeel","System");
if (laf.equalsIgnoreCase("System")) laf = UIManager.getSystemLookAndFeelClassName();
else if (laf.equalsIgnoreCase("Java")) laf = UIManager.getCrossPlatformLookAndFeelClassName();
if (!UIManager.getLookAndFeel().getClass().getName().equals(laf))
{
try
{
UIManager.setLookAndFeel(laf);
updateComponentTreeUI(menuPane);
if (menuBar != null) updateComponentTreeUI(menuBar);
}
catch (Exception x)
{}
}
setStatusMessage("Starting command manager");
ctm.start();
Rectangle bounds = getPreferredBounds();
// Once the frame is created the application will not stop unless we explicitly call
// System.exit, which does not report the exception back to the invoking application as
// neatly as just throwing an exception from main(). We delay creating the JFrame until
// the last minute to attempt to mitigate this problem.
JFrame frame = new JFrame(appProp.getProperty("title"));
frame.setIconImage(iconImage);
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e){ exit(); }
});
GlobalMouseListener gml = new GlobalMouseListener(frame);
gml.addMouseListener(new GlobalPopupListener());
frame.setContentPane(menuPane);
if (menuBar != null) frame.setJMenuBar(menuBar);
if (bounds == null) frame.pack();
else frame.setBounds(bounds);
// If there are any queued errors, display them now
List errors = null;
synchronized (queueLock)
{
errors = queueRunnable;
queueRunnable = null;
}
for (Iterator i = errors.iterator(); i.hasNext(); )
{
Runnable r = (Runnable) i.next();
SwingUtilities.invokeLater(r);
}
Runnable r = new Runnable()
{
public void run()
{
fireApplicationVisible(new ApplicationEvent(Application.this,ApplicationEvent.APPLICATION_VISIBLE));
}
};
SwingUtilities.invokeLater(r);
l.succeeded();
l = null;
return frame;
}
catch (InitializationException x)
{
l.failed(x);
throw x;
}
}
protected void init()
{
}
protected Rectangle getPreferredBounds()
{
//TODO: Check that we are not off the screen
return PropertyUtilities.getRectangle(getUserProperties(),"window",null);
}
public Properties getUserProperties()
{
if (userProperties == null)
{
userProperties = createUserProperties();
loadUserProperties(userProperties);
}
return userProperties;
}
protected Properties createUserProperties()
{
return new TranslatedProperties(getAppProperties());
}
protected XMLReader createXMLReader()
{
try
{
return SAXParserFactory.newInstance().newSAXParser().getXMLReader();
}
catch (SecurityException x) // JAXP doesn't work if downloaded into webstart
{
String className = getUserProperties().getProperty("SAXParser","org.apache.crimson.parser.XMLReaderImpl");
return (XMLReader) createObjectFromProperty(className,XMLReader.class);
}
catch (Exception x)
{
throw new RuntimeException("Error creating XML Parser",x);
}
}
protected XMLMenuBuilder createXMLMenuBuilder()
{
setStatusMessage("Adding menus");
URL xml = PropertyUtilities.getURL(getUserProperties(),"menuXML",null);
if (xml == null) throw new InitializationException("menuXML file not found");
try
{
XMLMenuBuilder builder = new ApplicationXMLMenuBuilder();
XMLReader reader = createXMLReader();
builder.build(xml,reader);
return builder;
}
catch (IOException x)
{
throw new InitializationException("IO Exception while reading "+xml,x);
}
catch (SAXException x)
{
throw new InitializationException("XML Exception while reading "+xml,x);
}
}
public XMLMenuBuilder getXMLMenuBuilder() throws InitializationException
{
if (xmlMenuBuilder == null) xmlMenuBuilder = createXMLMenuBuilder();
return xmlMenuBuilder;
}
public Properties getAppProperties()
{
if (appProperties == null) appProperties = createAppProperties();
return appProperties;
}
protected Properties createAppProperties() throws InitializationException
{
Properties prop;
try
{
prop = new TranslatedProperties(System.getProperties());
}
catch (SecurityException x) // May happen in unsigned webstart app
{
prop = new TranslatedProperties();
}
prop.setProperty("appName",appName);
try
{
loadDefaultProperties(prop);
String propFile = appName+".properties";
InputStream in = this.getClass().getResourceAsStream(propFile);
if (in != null)
{
prop.load(in);
in.close();
}
//else throw new InitializationException("Could not find application properties: "+propFile);
return prop;
}
catch (IOException x)
{
throw new InitializationException("Error initializing appplication properties",x);
}
}
protected void loadDefaultProperties(Properties app) throws IOException
{
// load Default Properties
InputStream in = Application.class.getResourceAsStream("Default.properties");
app.load(in);
in.close();
}
public String getAppName()
{
return appName;
}
private static Object createObjectFromProperty(String className, Class type) throws InitializationException
{
try
{
Class klass = Class.forName(className);
if (!type.isAssignableFrom(klass)) throw new InitializationException("Class "+className+" is not of type "+type);
return klass.newInstance();
}
catch (InitializationException x)
{
throw x;
}
catch (Throwable t)
{
throw new InitializationException("Error creating "+className,t);
}
}
public void addApplicationListener(ApplicationListener l)
{
listenerList.add(ApplicationListener.class,l);
}
public void removeApplicationListener(ApplicationListener l)
{
listenerList.remove(ApplicationListener.class,l);
}
protected void fireInitializationComplete(ApplicationEvent event)
{
if (listenerList.getListenerCount(ApplicationListener.class) > 0)
{
ApplicationListener[] listener = (ApplicationListener[]) listenerList.getListeners(ApplicationListener.class);
for (int i=0; i<listener.length; i++)
{
listener[i].initializationComplete(event);
}
}
}
protected void fireApplicationVisible(ApplicationEvent event)
{
// if (listenerList.getListenerCount(ApplicationListener.class) > 0)
// {
// ApplicationListener[] listener = (ApplicationListener[]) listenerList.getListeners(ApplicationListener.class);
// for (int i=0; i<listener.length; i++)
// {
// listener[i].applicationVisible(event);
// }
// }
}
protected void fireAboutToExit(ApplicationEvent event)
{
if (listenerList.getListenerCount(ApplicationListener.class) > 0)
{
ApplicationListener[] listener = (ApplicationListener[]) listenerList.getListeners(ApplicationListener.class);
for (int i=0; i<listener.length; i++)
{
listener[i].aboutToExit(event);
}
}
}
public JMenuBar getMenuBar()
{
return menuBar;
}
public void updateUI()
{
super.updateUI();
if (!statusBarVisible && statusBar != null) updateComponentTreeUI(statusBar);
if (!toolBarVisible && toolBarHolder != null) updateComponentTreeUI(toolBarHolder);
}
/**
* A simple minded look and feel change: ask each node in the tree
* to <code>updateUI()</code> -- that is, to initialize its UI property
* with the current look and feel. Unlike SwingUtilities.updateComponentTreeUI
* this method updates each component's children before updating the component itself,
* making it easier for components to fine tune their children's look and feel's.
*/
public static void updateComponentTreeUI(Component c)
{
updateComponentTreeUI0(c);
c.invalidate();
c.validate();
c.repaint();
}
private static void updateComponentTreeUI0(Component c)
{
Component[] children = null;
if (c instanceof JMenu)
{
children = ((JMenu)c).getMenuComponents();
}
else if (c instanceof Container)
{
children = ((Container)c).getComponents();
}
if (children != null)
{
for(int i = 0; i < children.length; i++)
{
updateComponentTreeUI0(children[i]);
}
}
if (c instanceof JComponent)
{
((JComponent) c).updateUI();
}
}
// public void runAfterApplicationAppears(Runnable r)
// {
// synchronized (queueLock)
// {
// if (queueRunnable != null) queueRunnable.add(r);
// else SwingUtilities.invokeLater(r);
// }
// }
private static Application theApp;
private String appName;
private ServiceManager serviceManager;
private Properties appProperties;
private Properties userProperties;
private XMLMenuBuilder xmlMenuBuilder;
private CommandProcessor commandProcessor;
private CommandTargetManager commandTargetManager;
private CommandLine commandLine;
private HelpService helpService;
private StatusBar statusBar;
private JToolBar toolBar;
private JMenuBar menuBar;
private JPanel toolBarHolder;
private JPanel menuPane;
private Hashtable rflHash;
private boolean statusBarVisible = false;
private boolean toolBarVisible = false;
private boolean init = false;
private InitializationListener l = new DefaultInitializationListener();
private List queueRunnable;
private Object queueLock = new Object();
private class DefaultInitializationListener implements InitializationListener
{
public void setStage(String message)
{ System.out.println(message); }
public void failed(Throwable t)
{ System.out.println("Initialization failed "+t); }
public void succeeded()
{ System.out.println("Initialization complete"); }
}
/**
* Extends CommandProcessor to handle some standard Application commands.
* Also allows the onXXX() enableXXX() methods to be declared on the application
* itself, as well as the command processor, as a convenience for simple applications.
*/
public class ApplicationCommandProcessor extends CommandProcessor
{
public void onExit()
{
exit();
}
public void onAbout()
{
about();
}
public void onHelpContents()
{
showHelpContents();
}
public void onHelpSearch()
{
showHelpSearch();
}
public void onHelpIndex()
{
showHelpIndex();
}
public void onShowStatusBar(boolean show)
{
setShowStatusBar(show);
}
public void enableShowStatusBar(BooleanCommandState state)
{
state.setSelected(statusBarVisible);
state.setEnabled(true);
}
public void onShowToolBar(boolean show)
{
setShowToolBar(show);
}
public void enableShowToolBar(BooleanCommandState state)
{
state.setSelected(toolBarVisible);
state.setEnabled(toolBarHolder != null);
}
public CommandTarget acceptCommand(String command)
{
CommandTarget retValue = super.acceptCommand(command);
if (retValue != null) return retValue;
return super.acceptCommand(getApplication().getClass(),command);
}
protected void invoke(Method method, Object[] args) throws IllegalAccessException, InvocationTargetException
{
if (method.getDeclaringClass().isInstance(getApplication()))
{
method.invoke(getApplication(),args);
}
else super.invoke(method, args);
}
}
protected class ApplicationCommandTargetManager extends CommandTargetManager
{
public void handleCommandError(Throwable t)
{
error("Error during command processing",t);
}
}
private class ApplicationXMLMenuBuilder extends XMLMenuBuilder
{
protected JMenuItem createMenuItem(String className, String name, String type, String command) throws SAXException
{
JMenuItem result = super.createMenuItem(className, name, type, command);
getCommandTargetManager().add(new CommandSourceAdapter(result));
return result;
}
protected AbstractButton createToolBarItem(String className, String name, String type, String command) throws SAXException
{
AbstractButton result = super.createToolBarItem(className, name, type, command);
getCommandTargetManager().add(new CommandSourceAdapter(result));
return result;
}
}
public static class InitializationException extends RuntimeException
{
public InitializationException(String message, Throwable detail)
{
super(message, detail);
}
public InitializationException(String message)
{
super(message);
}
}
public interface InitializationListener
{
void setStage(String stage);
void failed(Throwable x);
void succeeded();
}
public class AppPrintPreview extends PrintPreview
{
public void setPrintable(Printable painter) throws PrinterException
{
setPrintable(painter,getServiceManager().getDefaultPage());
}
protected boolean onPrint(Pageable document) throws PrinterException
{
return getServiceManager().print(document);
}
protected void onError(PrinterException x)
{
error(this,"Print Error",x);
}
}
}