All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.globalmentor.swing.ApplicationFrame Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 1996-2009 GlobalMentor, Inc. 
 *
 * 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 com.globalmentor.swing;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import com.globalmentor.application.Application;
import com.globalmentor.log.Log;

/**
 * Main frame parent class for an application.
 * 

* This class requires that the content pane be an instance of {@link ApplicationContentPane}. *

*

* If an {@link Application} is set for the frame, the close operation changes to {@link WindowConstants#EXIT_ON_CLOSE}. (This is essential to work around a JDK * bug that under certain instances runs a daemon thread that prevents the JVM from exiting, even if the main program thread has finished and the sole frame has * closed.) If there is no {@link Application} set, the close operation remains the default, {@link WindowConstants#DISPOSE_ON_CLOSE}. *

*

* This class maintains the default close operation of {@link WindowConstants#DO_NOTHING_ON_CLOSE}. This allows the class listen to window events and perform * consistent canClose() checks for all methods of closing. This is all handled transparently—once closing should occur, the local default * close operation setting is honored as normal. *

*

* If an application is provided, the application's preference node is used to store user preferences. *

* @author Garret Wilson * @see Application * @see AbstractSwingApplication */ public class ApplicationFrame extends BasicFrame { /** * The application this frame represents, or null if there is no application information. */ private final AbstractSwingApplication application; /** * @return The application this frame represents, or null if there is no application information. */ public final AbstractSwingApplication getApplication() { return application; } // /** The action for file|exit; defaults to getExitAction(). */ private Action fileExitAction; /** The action for showing an about box. */ private final Action aboutAction; /** @return The action for showing an about box. */ public Action getAboutAction() { return aboutAction; } /** * The proxy action for closing the frame or exiting the application. *

* Defaults to getExitAction() if there is an application, or getCloseAction() if there is no application. *

*/ private final ProxyAction closeProxyAction; /** * @return The proxy action for closing the frame and optionally exiting the application, depending on whether an application is present. * @see #getApplication() */ public Action getCloseProxyAction() { return closeProxyAction; } /** The action for exiting the application. */ private final Action exitAction; /** @return The action for exiting the application. */ public Action getExitAction() { return exitAction; } /** * Constructs a string appropriate for showing on the title of the frame. *

* This version returns the application name. *

* @return A title to display on the frame. */ protected String constructTitle() { final Application application = getApplication(); final String title; if(application != null) { //if we have an application return getApplication().getName(); //get the application's label TODO add the capability for a label, which may be more decorated (e.g. with the trademark symbol) than just a name } else { //if we have no application title = super.constructTitle(); //use the default title } return title; //return the title we discovered } /** * Sets the contentPane property. * @param contentPane the contentPane object for this frame * @throws IllegalComponentStateException (a runtime exception) if the content pane parameter is null. * @throws ClassCastException Thrown if the content pane is not an instance of ApplicationContentPane. * @see #ApplicationContentPane */ public void setContentPane(final Container contentPane) { super.setContentPane((ApplicationContentPane)contentPane); //make sure the content pane is of the correct type } /** @return The content pane as an application content pane; convenience method. */ public ApplicationContentPane getApplicationContentPane() { return (ApplicationContentPane)getContentPane(); //return the content pane cast to an application content pane } /** @return The toolbar, or null if there is no toolbar. */ public BasicToolBar getToolBar() { return getApplicationContentPane().getToolBar(); } /** * Sets the toolbar. * @param newToolBar The new toolbar to use, or null if there should be no toolbar. */ protected void setToolBar(final BasicToolBar newToolBar) { getApplicationContentPane().setToolBar(newToolBar); } /** @return The application status bar. */ public StatusBar getStatusBar() { return getApplicationContentPane().getStatusBar(); } /** * Sets the status bar. * @param newStatusBar The new status bar to use, or null if there should be no status bar. */ protected void setStatusBar(final StatusBar newStatusBar) { getApplicationContentPane().setStatusBar(newStatusBar); } /** Default constructor. */ public ApplicationFrame() { this((AbstractSwingApplication)null); //construct the frame with no application } /** * Application constructor. * @param application The application this frame represents, or null if there is no application information available or this frame doesn't * represent an application. */ public ApplicationFrame(final AbstractSwingApplication application) { this(application, true); //create an application frame with a default application panel and initialize } /** * Constructor with a default panel and optional initialization. * @param initialize true if the panel should initialize itself by calling the initialization methods. */ public ApplicationFrame(final boolean initialize) { this((AbstractSwingApplication)null, initialize); //construct the frame with no application } /** * Application constructor with a content pane and optional initialization. * @param application The application this frame represents, or null if there is no application information available or this frame doesn't * represent an application. * @param initialize true if the panel should initialize itself by calling the initialization methods. */ public ApplicationFrame(final AbstractSwingApplication application, final boolean initialize) { this(application, null, initialize); //create an application frame with the default content pane } /** * Application component constructor. * @param applicationComponent The component to be used as application component, or null if the default application component should be used. */ public ApplicationFrame(final Component applicationComponent) { this(null, applicationComponent); //construct the frame with no application } /** * Application and component constructor. * @param application The application this frame represents, or null if there is no application information available or this frame doesn't * represent an application. * @param applicationComponent The component to be used as application component, or null if the default application component should be used. */ public ApplicationFrame(final AbstractSwingApplication application, final Component applicationComponent) { this(application, applicationComponent, true); //construct and initialize the frame } /** * Application component constructor with optional initialization. * @param applicationComponent The component to be used as application component, or null if the default application component should be used. * @param initialize true if the panel should initialize itself by calling the initialization methods. */ public ApplicationFrame(final Component applicationComponent, final boolean initialize) { this(null, applicationComponent, initialize); //construct the frame with no application } /** * Application, and component constructor with optional initialization. * @param application The application this frame represents, or null if there is no application information available or this frame doesn't * represent an application. * @param applicationComponent The component to be used as application component, or null if the default application component should be used. * @param initialize true if the panel should initialize itself by calling the initialization methods. */ public ApplicationFrame(final AbstractSwingApplication application, final Component applicationComponent, final boolean initialize) { super(false); //construct the parent class, but don't initialize it setContentPane(new ApplicationContentPane(applicationComponent, false, false)); //create a special application content pane and place the application component inside it this.application = application; //store the application if(application != null) { //if this frame represents an application setDefaultCloseOperation(EXIT_ON_CLOSE); //exit when the frame closes try { setPreferences(application.getPreferences()); //use the application preference node } catch(SecurityException securityException) { //if we can't access preferences Log.warn(securityException); //warn of the security problem } } exitAction = new ExitAction(); //create the exit action closeProxyAction = new ProxyAction(application != null ? getExitAction() : getCloseAction()); //create the close proxy action, proxying the exit action if we have an application aboutAction = new AboutAction(); if(initialize) //if we should initialize initialize(); //initialize the frame } /** * Initializes actions in the action manager. * @param actionManager The implementation that manages actions. */ protected void initializeActions(final ActionManager actionManager) { super.initializeActions(actionManager); //do the default initialization final AbstractSwingApplication application = getApplication(); //get our application, if there is one final Action fileMenuAction = ActionManager.getFileMenuAction(); actionManager.addMenuAction(fileMenuAction); //file actionManager.addMenuAction(fileMenuAction, getCloseProxyAction()); //file|close/exit if(application != null) { //if we have an application final Action helpMenuAction = ActionManager.getHelpMenuAction(); actionManager.addMenuAction(helpMenuAction); //help actionManager.addMenuAction(helpMenuAction, getAboutAction()); //help|about } } /** * Initializes the user interface. Any derived class that overrides this method should call this version. */ /*TODO del if not needed protected void initializeUI() { super.initializeUI(); //do the default UI initialization } */ /** * Updates the states of the actions, including enabled/disabled status, proxied actions, etc. */ /*TODO fix if needed protected void updateStatus() { super.updateStatus(); //update the status normally //TODO fix this; this isn't good, because if a child class uses another save action, such as a proxied action, this will screw things up //TODO fix final RDFResourceState description=getDocumentDescription(); //see what document is being described //TODO fix getFileSaveAction().setEnabled(description!=null && description.isModified()); //only enable saving when there is a document that's modified } */ /** Shows information about the application. */ public void about() { /*TODO fix about box; determine how applications will store properties final AboutPanel aboutPanel=new AboutPanel(getApplication()); //create a new about panel for the application //determine a title for the dialog, based upon the application title final String dialogTitle="About"+(aboutPanel.getTitle()!=null ? " "+aboutPanel.getTitle() : ""); //TODO i18n //have an option pane create and show a new dialog using our about panel BasicOptionPane.showMessageDialog(this, aboutPanel, dialogTitle, JOptionPane.INFORMATION_MESSAGE); //TODO check and see why we originally had a more complex version */ } /** * Determines whether the frame can close. This version attempts to save any application configuration information. * @return true if the frame can close. */ /*TODO del; transferred to Application public boolean canClose() { boolean canClose=super.canClose(); //try to perform the default closing functionality if(canClose) { //if we can close, make sure the configuration has been saved //if there is configuration information and it has been modified if(getApplication()!=null && getApplication().getConfiguration()!=null && getApplication().getConfiguration().isModified()) { try { getApplication().getConfiguration().store(); //try to store the configuration information; if we were unsuccessful } catch(IOException ioException) { //if there is an error saving the configuration getApplication().displayError(this, ioException); //alert the user of the error //ask if we can close even though we can't save the configuration information canClose=JOptionPane.showConfirmDialog(this, "Unable to save configuration information; are you sure you want to close?", "Unable to save configuration", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION; //TODO i18n } } } return canClose; //return whether we can close } */ /** * Determines whether the frame and application can close. * @return true if the application frame and application can close. * @see ToolStatusPanel#canClose * @see #getDocumentDescription * @see #canClose(RDFResourceState) */ /*TODO fix public boolean canClose() { return canCloseFile(); //return whether or not the current file can be closed } */ /** * @return true if the currently open file can be closed, else false if closing should be cancelled. If the content pane is an * ToolStatusPanel, its canClose() method is called. If there is a document description, the frame's canClose() * method is called for the description. */ /*TODO fix public boolean canCloseFile() { boolean canClose=true; //default to being able to be closed if(getContentPane() instanceof CanClosable) { //if the content pane knows how to ask about closing canClose=((CanClosable)getContentPane()).canClose(); //ask if the content pane can be closed } if(canClose) { //if everything looks like we can close so far final RDFResourceState description=getDocumentDescription(); //get the current document description if(description!=null && description!=getContentPane()) //if we have a document description, and the description isn't the content pane (otherwise we would call canClose() twice) canClose=canClose(description); //see if we can close the description } return canClose; //return whether we can close the frame } */ /** * Exits the application with the given status. This version asks the application to exit, if there is an application. * @param status The exit status. * @see Application#exit(int) */ protected void exit(final int status) { if(getApplication() != null) { //if there is an application getApplication().exit(status); //ask the application to exit } else { //if there is no application super.exit(status); //exit in the default way, if there is no application or the application couldn } } /** * The content pane for the application frame. The content pane allows for a toolbar and status panel. The action manager it returns will be the action * manager of its contained ActionManaged, if any. * @author Garret Wilson */ protected class ApplicationContentPane extends ToolStatusPanel { /** * @return The action manager of the content component, if the content component is action managed; otherwise, the defualt action manager. * @see ActionManaged */ public ActionManager getActionManager() { final Component contentComponent = getContentComponent(); //get the content component return contentComponent instanceof ActionManaged ? ((ActionManaged)contentComponent).getActionManager() : getActionManager(); //return the content component's action manager if it has one } /** Default constructor. */ public ApplicationContentPane() { this(true, true); //default to having a toolbar and a status bar } /** * Constructor that allows options to be set, such as the presence of a status bar. * @param hasToolBar Whether this panel should have a toolbar. * @param hasStatusBar Whether this panel should have a status bar. */ public ApplicationContentPane(final boolean hasToolBar, final boolean hasStatusBar) { this(hasToolBar, hasStatusBar, true); //construct and initialize the panel } /** * Initialization constructor that allows options to be set, such as the presence of a status bar. * @param hasToolBar Whether this panel should have a toolbar. * @param hasStatusBar Whether this panel should have a status bar. * @param initialize true if the panel should initialize itself by calling the initialization methods. */ public ApplicationContentPane(final boolean hasToolBar, final boolean hasStatusBar, final boolean initialize) { this(null, hasToolBar, hasStatusBar, initialize); //construct the panel with no content component } /** * Application component constructor. * @param applicationComponent The new component for the center of the panel. */ public ApplicationContentPane(final Component applicationComponent) { this(applicationComponent, true, true); //do the default construction with a toolbar and a status bar } /** * Application component constructor that allows options to be set. * @param applicationComponent The new component for the center of the panel. * @param hasToolBar Whether this panel should have a toolbar. * @param hasStatusBar Whether this panel should have a status bar. */ public ApplicationContentPane(final Component applicationComponent, final boolean hasToolBar, final boolean hasStatusBar) { this(applicationComponent, hasToolBar, hasStatusBar, true); //construct and automatically initialize the object } /** * Application component constructor that allows options to be set. * @param applicationComponent The new component for the center of the panel. * @param hasToolBar Whether this panel should have a toolbar. * @param hasStatusBar Whether this panel should have a status bar. * @param initialize true if the panel should initialize itself by calling the initialization methods. */ public ApplicationContentPane(final Component applicationComponent, final boolean hasToolBar, final boolean hasStatusBar, final boolean initialize) { super(applicationComponent, hasToolBar, hasStatusBar, initialize); //construct the parent class without intializing TODO create a method to create a default center panel /*TODO fix toolBarPosition=BorderLayout.NORTH; //default to the toolbar in the north statusBarPosition=BorderLayout.SOUTH; //default to the status bar in the south toolBar=hasToolBar ? createToolBar() : null; //create a toolbar if we should have one statusBar=hasStatusBar ? createStatusBar() : null; //create a status bar if we should have one if(initialize) //if we should initialize the panel initialize(); //initialize everything */ } } /** Action for exiting the application. */ protected class ExitAction extends AbstractAction { /** Default constructor. */ public ExitAction() { super("Exit"); //create the base class TODO i18n putValue(SHORT_DESCRIPTION, "Exit the application"); //set the short description TODO i18n putValue(LONG_DESCRIPTION, "Exit the application."); //set the long description TODO i18n putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_X)); //set the mnemonic key TODO i18n putValue(SMALL_ICON, IconResources.getIcon(IconResources.EXIT_ICON_FILENAME)); //load the correct icon putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F4, Event.ALT_MASK)); //add the accelerator putValue(ActionManager.MENU_ORDER_PROPERTY, new Integer(ActionManager.FILE_EXIT_MENU_ACTION_ORDER)); //set the order } /** * Called when the action should be performed. * @param actionEvent The event causing the action. */ public void actionPerformed(final ActionEvent actionEvent) { close(); //close the frame; this assumes that setDefaultCloseOperation has been set to DISPOSE_ON_CLOSE or EXIT_ON_CLOSE } } /** Action for showing the help about dialog. */ protected class AboutAction extends AbstractAction { /** Default constructor. */ public AboutAction() { super("About..."); //create the base class TODO i18n putValue(SHORT_DESCRIPTION, "About the application"); //set the short description TODO i18n putValue(LONG_DESCRIPTION, "Show more information about the application."); //set the long description TODO i18n putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_A)); //set the mnemonic key TODO i18n putValue(SMALL_ICON, IconResources.getIcon(IconResources.INFO_ICON_FILENAME)); //load the correct icon putValue(ActionManager.MENU_ORDER_PROPERTY, new Integer(ActionManager.HELP_ABOUT_MENU_ACTION_ORDER)); //set the order } /** * Called when the action should be performed. * @param actionEvent The event causing the action. */ public void actionPerformed(final ActionEvent actionEvent) { about(); //show the help about } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy