/* * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved. * Copyright (C) 2010 Illya Yalovyy. All rights reserved. * Copyright (C) 2012 Peransin Nicolas. All rights reserved. * Use is subject to license terms. */ package org.mypsycho.swing.app; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.RootPaneContainer; import org.mypsycho.swing.app.session.SessionBehaviour; import org.mypsycho.swing.app.session.SessionStorage; import org.mypsycho.swing.app.utils.SwingHelper; /** * An application base class for simple GUIs with one primary JFrame. * <p> * This class takes care of component property injection, exit processing, * and saving/restoring session state in a way that's appropriate for * simple single-frame applications. The application's JFrame is created * automatically, with a WindowListener that calls exit() when the * window is closed. Session state is stored when the application * shuts down, and restored when the GUI is shown. * <p> * To use {@code SingleFrameApplication}, one need only override * {@code startup}, create the GUI's main panel, and apply * {@code show} to that. Here's an example: * <pre> *class MyApplication extends SingleFrameApplication { * @Override protected void startup() { * show(new JLabel("Hello World")); * } *} * </pre> * The call to {@code show} in this example creates a JFrame (named * "mainFrame"), that contains the "Hello World" JLabel. Before the * frame is made visible, the properties of all of the components in * the hierarchy are initialized with * {@link ResourceMap#injectComponents ResourceMap.injectComponents} * and then restored from saved session state (if any) with * {@link SessionStorage#restore SessionStorage.restore}. * When the application shuts down, session state is saved. * <p> * A more realistic tiny example would rely on a ResourceBundle for * the JLabel's string and the main frame's title. The automatic * injection step only initializes the properties of named * components, so: * <pre> * class MyApplication extends SingleFrameApplication { * @Override protected void startup() { * show(new JLabel()); * } * } * </pre> * The ResourceBundle should contain definitions for all of the * standard Application resources, as well the main frame's title * and the label's text. Note that the JFrame that's implicitly * created by the {@code show} method is named "mainFrame". * <pre> * # resources/MyApplication.properties * Application.id = MyApplication * Application.title = My Hello World Application * Application.version = 1.0 * Application.description = An example of SingleFrameApplication * Application.lookAndFeel = system * Application.icon=app_icon.png * * view(mainFrame).title = ${Application.title} ${Application.version} * # 'component' is the default id * view(mainFrame)(component).text = Hello World * </pre> */ public abstract class SingleFrameApplication extends Application { public static final String MAIN_FRAME_NAME = FrameView.MAIN_FRAME_NAME; /** * */ public SingleFrameApplication() { setCommonBehaviour(new SessionBehaviour()); } /** * Return the JFrame used to show this application. * <p> * The frame's name is set to "mainFrame", its title is * initialized with the value of the {@code Application.title} * resource and a {@code WindowListener} is added that calls * {@code exit} when the user attempts to close the frame. * * <p> * This method may be called at any time; the JFrame is created lazily * and cached. For example: * <pre> * protected void startup() { * getMainFrame().setJMenuBar(createMenuBar()); * show(createMainPanel()); * } * </pre> * * @return this application's main frame * @see #setMainFrame * @see #show * @see JFrame#setName * @see JFrame#setTitle * @see JFrame#addWindowListener */ public final JFrame getMainFrame() { return getMainView().getFrame(); } /** * Sets the JFrame use to show this application. * <p> * This method should be called from the startup method by a * subclass that wants to construct and initialize the main frame * itself. Most applications can rely on the fact that {code * getMainFrame} lazily constructs the main frame and initializes * the {@code mainFrame} property. * <p> * If the main frame property was already initialized, either * implicitly through a call to {@code getMainFrame} or by * explicitly calling this method, an IllegalStateException is * thrown. If {@code mainFrame} is null, an IllegalArgumentException * is thrown. * <p> * This property is bound. * * @param mainFrame the new value of the mainFrame property * @see #getMainFrame */ protected final void setMainFrame(JFrame mainFrame) { getMainView().setFrame(mainFrame); } /** * Show the specified component in the {@link #getMainFrame main frame}. * Typical applications will call this method after constructing their * main GUI panel in the {@code startup} method. * <p> * Before the main frame is made visible, the properties of all of * the components in the hierarchy are initialized with {@link * ResourceMap#injectComponents ResourceMap.injectComponents} and * then restored from saved session state (if any) with {@link * SessionStorage#restore SessionStorage.restore}. When the * application shuts down, session state is saved. * <p> * Note that the name of the lazily created main frame (see * {@link #getMainFrame getMainFrame}) is set by default. * Session state is only saved for top level windows with * a valid name and then only for component descendants * that are named. * <p> * Throws an IllegalArgumentException if {@code c} is null * * @param c the main frame's contentPane child */ protected void show(JComponent c) { SwingHelper.assertNotNull("component", c); View view = getMainView(); view.setComponent(c); show(view); } /** * Use the root component to be shown in the main frame. * <p> * Root component must be a a JComponent * </p> * * @param c the main frame's contentPane child */ protected void show(SwingHelper h) { show(h.root().<JComponent>get()); } /* Prototype support for the View type */ private FrameView mainView = null; /** * Gets the main view of the application. * * @return the main view of the application */ public FrameView getMainView() { if (mainView == null) { mainView = new FrameView(this); registerCommonBehavior(mainView); } return mainView; } protected View createView(RootPaneContainer c) { if ((mainView == null) && (c instanceof JFrame)) { getMainView().setFrame((JFrame) c); return getMainView(); } return super.createView(c); } @Override public void show(View view) { if ((mainView == null) && (view instanceof FrameView)) { mainView = (FrameView) view; registerCommonBehavior(mainView); } super.show(view); } }