package net.xoetrope.swing.app;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Toolkit;
import java.net.URL;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import net.xoetrope.xui.XProject;
import net.xoetrope.swing.SwingWidgetAdapter;
import net.xoetrope.xui.XApplicationContext;
import net.xoetrope.xui.XStartupObject;
import java.awt.Component;
import java.awt.Container;
import java.awt.SystemColor;
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Hashtable;
import javax.swing.JPanel;
import net.xoetrope.swing.docking.XCardPanel;
import net.xoetrope.swing.docking.XDockable;
import net.xoetrope.swing.docking.XDockingPanel;
import net.xoetrope.swing.docking.XDockingSideBar;
import net.xoetrope.xui.PageSupport;
import net.xoetrope.xui.XPage;
import net.xoetrope.xui.XProjectManager;
import net.xoetrope.xui.helper.XTranslator;
import net.xoetrope.xui.style.XStyle;
import net.xoetrope.xui.style.XStyleEx;
import net.xoetrope.xui.style.XStyleManager;
import org.jdesktop.swingx.MultiSplitLayout;
import org.jdesktop.swingx.JXMultiSplitPane;
import org.jdesktop.swingx.painter.Painter;
/**
* <p>A startup class for a desktop/MDI style of application.</p>
* <p>For information on the initial docking layout please see the article
* <a ref="http://today.java.net/pub/a/today/2006/03/23/multi-split-pane.html">
* MultiSplitPane: Splitting Without Nesting</a></p>
* <p>Copyright: Copyright (c) Xoetrope Ltd., 1998-2003<br>
* License: see license.txt
* @version $Revision: 1.1 $
*/
public class XDockingApp extends JFrame implements XStartupObject
{
protected XApplicationContext applicationContext;
protected XCardPanel cardPanel;
protected JPanel dockingPanel;
protected JXMultiSplitPane multiSplitPane;
protected XDockingSideBar leftSidebar;
protected XDockingSideBar rightSidebar;
protected XDockingSideBar bottomSidebar;
protected PageSupport northDecoration;
protected JMenuBar menuBar;
protected XProject currentProject;
protected String defaultLayoutDef =
"(COLUMN " +
" (ROW name=top weight=0.7 " +
" (LEAF name=left weight=0.1) " +
" (LEAF weight=0.8 name=content) " +
" (LEAF name=right weight=0.1)" +
" ) " +
" (LEAF name=bottom weight=0.3)" +
")";
private boolean exclusiveView;
/**
* main method to be invoked as an application. This method is invoked as the
* entry point to the 'Application', it is not used if an Applet is being
* launched. This method establishes the frame within which the application
* runs. If overloading this method remeber to call the setup method.
* @param args the command line arguments
*/
public static void main( String args[] )
{
final String[] theArgs = args;
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater( new Runnable()
{
public void run()
{
createAndShowGUI( theArgs );
}
});
}
/**
* Creates a new instance of XDesktopApp
* @param args the startup arguments
*/
public XDockingApp( String[] args )
{
super( "XUI" );
currentProject = XProjectManager.getCurrentProject( this );
// Setup the docking framework
dockingPanel = new JPanel();
dockingPanel.setLayout( new BorderLayout());
// Setup the sidebars
Container glassPane = (Container)getRootPane().getGlassPane();
leftSidebar = new XDockingSideBar( glassPane, "west" );
rightSidebar = new XDockingSideBar( glassPane, "east" );
bottomSidebar = new XDockingSideBar( glassPane, "south" );
// Setup the default layout
MultiSplitLayout.Node modelRoot = MultiSplitLayout.parseModel( defaultLayoutDef );
multiSplitPane = new JXMultiSplitPane();
multiSplitPane.setDividerSize( 5 );
multiSplitPane.getMultiSplitLayout().setModel( modelRoot );
MultiSplitLayout multiSplitLayout = multiSplitPane.getMultiSplitLayout();
multiSplitLayout.setLayoutMode( MultiSplitLayout.USER_MIN_SIZE_LAYOUT );
multiSplitLayout.layoutByWeight( multiSplitPane );
cardPanel = new XCardPanel( multiSplitPane );
dockingPanel.add( leftSidebar, BorderLayout.WEST );
dockingPanel.add( rightSidebar, BorderLayout.EAST );
dockingPanel.add( bottomSidebar, BorderLayout.SOUTH );
dockingPanel.add( cardPanel, BorderLayout.CENTER );
//Set up the GUI.
setContentPane( dockingPanel );
SwingWidgetAdapter.getInstance();
applicationContext = new XApplicationContext( this, "net.xoetrope.swing.app.XDockableFrame", args );
// Style the panel
XStyleManager sm = currentProject.getStyleManager();
if ( sm.hasStyle( "dockingApp" )) {
XStyle dockingStyle = sm.getStyle( "dockingApp" );
multiSplitPane.setBackground( dockingStyle.getStyleAsColor( XStyle.COLOR_BACK ));
XStyleEx exStyle = (XStyleEx)dockingStyle;
int idx = exStyle.getStyleIndex( "padding" );
if ( idx > 0 ) {
int padding = new Integer( exStyle.getStyleValue( idx ).toString()).intValue();
multiSplitPane.setDividerSize( padding );
}
}
// Apply styles to the sidebars
applyStyles( leftSidebar );
applyStyles( rightSidebar );
applyStyles( bottomSidebar );
}
/**
* Carry out any post creation styling. This method is called one the
* application frameowrk has been setup. By default the method sets the
* colours of the component as the style manager is now available.
*/
public void applyStyles( XDockingSideBar target )
{
Color sidebarBkColor = SystemColor.control;
Color sidebarTextColor = SystemColor.controlText;
Color sidebarRolloverTextColor = SystemColor.controlText;
XStyleManager sm = currentProject.getStyleManager();
if ( sm.hasStyle( "dockingSidebar" )) {
XStyle xstyle = sm.getStyle( "dockingSidebar" );
sidebarBkColor = xstyle.getStyleAsColor( XStyle.COLOR_BACK );
sidebarTextColor = xstyle.getStyleAsColor( XStyle.COLOR_FORE );
xstyle = sm.getStyle( "dockingSidebar/active" );
sidebarRolloverTextColor = xstyle.getStyleAsColor( XStyle.COLOR_FORE );
}
target.applyStyles( sidebarBkColor, sidebarTextColor, sidebarRolloverTextColor );
}
/**
* Set a background painter for the empty docking panel
* @param painter a background painter
*/
public void setBackgroundPainter( Painter p )
{
multiSplitPane.setBackgroundPainter( p );
}
/**
* Get the side bar for a particular object
* @param key the key for looking up the sidebar
* @return the sidebar
*/
public XDockingSideBar getSidebar( String key )
{
String keyLower = key.toLowerCase();
if ( keyLower.equals( "south" ) || keyLower.equals( "bottom" ))
return bottomSidebar;
else if ( keyLower.equals( "east" ) || keyLower.equals( "right" ))
return rightSidebar;
else if ( keyLower.equals( "west" ) || keyLower.equals( "left" ))
return leftSidebar;
else
return null;
}
/**
* Set the visibility of the sidebars and toolbars
* @param state true to make the decorations visible
*/
public void setDecorationsVisible( boolean state )
{
leftSidebar.setVisible( state );
rightSidebar.setVisible( state );
bottomSidebar.setVisible( state );
if ( northDecoration != null )
((Component)northDecoration).setVisible( state );
}
/**
* <p>Display a window decoration, for example a toolbar.</p>
* <p>This method only sets
* the decoration for the NORTH constraint. It is intended for setting a
* toolbar.</p>
* @param page the new page
* @param constraint a value controlling how and where the decoration is
* displayed, this value is application type specific
* @return the page being displayed or null if the constraint is not handled
* @deprecated use addDecoration
*/
public Object displayDecoration( PageSupport page, String constraint )
{
return addDecoration( page, constraint );
}
/**
* <p>Display a window decoration, for example a toolbar.</p>
* <p>This method only sets
* the decoration for the NORTH constraint. It is intended for setting a
* toolbar.</p>
* @param page the new page
* @param constraint a value controlling how and where the decoration is
* displayed, this value is application type specific
* @return the page being displayed or null if the constraint is not handled
*/
public Object addDecoration( PageSupport page, String constraint )
{
String key = constraint.toLowerCase();
if ( key.equals( "north" ) || key.equals( "top" )) {
northDecoration = page;
dockingPanel.add( (Component)page, BorderLayout.NORTH );
return page;
}
return null;
}
/**
* Set the visibility of a border layout's component or so called decoration
* @param constraint must be "NORTH" to have any effect
* @param visible the visibility state
*/
public void setDecorationVisibility( String constraint, boolean visible )
{
Component decoration = null;
String key = constraint.toLowerCase();
if ( key.equals( "north" ) || key.equals( "top" ))
decoration = (Component)northDecoration;
else if ( key.equals( "south" ) || key.equals( "bottom" ))
decoration = bottomSidebar;
else if ( key.equals( "east" ) || key.equals( "right" ))
decoration = leftSidebar;
else if ( key.equals( "west" ) || key.equals( "left" ))
decoration = rightSidebar;
if ( decoration != null ) {
decoration.setVisible( visible );
doLayout();
}
}
/**
* Show one of the content panels as the exclusive content, hiding the
* sidebars, headers and other decorations
* @param comp the component to display exclusively in the content area
* @param state true for an exclusive display, false to restore the normal display
*/
public void showExclusive( Object comp, boolean state )
{
exclusiveView = state;
setDecorationsVisible( !state );
XDockable dockable = findDockable( comp );
if ( dockable != null ) {
dockable.header.zoomPanel();
// Hide the header panel
dockable.header.getParent().setVisible( !state );
Object mo = currentProject.getObject( "MenuBar" );
XStartupObject startupObject = currentProject.getStartupObject();
if ( state )
startupObject.setApplicationMenuBar( null );
else
startupObject.setApplicationMenuBar( mo );
}
cardPanel.doLayout();
multiSplitPane.doLayout();
repaint();
}
/**
* Get the package name for the default widget set
*/
public String getWidgetClassPackage()
{
return XPage.XUI_SWING_PACKAGE;
}
/**
* Get a startup parameter
* @param param the name of the parameter
*/
public String getParameter( String param )
{
return "";
}
/**
* Get the content pane used by XUI - the container in which pages are
* displayed
* @return the page container
*/
public Object getContentPaneEx()
{
return multiSplitPane;
}
/**
* Get the parent container/object
* @return the parent
*/
public Object getParentObject()
{
return super.getParent();
}
/**
* Gets the URL of the document in which this applet is embedded.
* For example, suppose an applet is contained
* within the document:
* <blockquote><pre>
* http://java.sun.com/products/jdk/1.2/index.html
* </pre></blockquote>
* The document base is:
* <blockquote><pre>
* http://java.sun.com/products/jdk/1.2/index.html
* </pre></blockquote>
*
* @return the {@link java.net.URL} of the document that contains this
* applet.
* @see java.applet.Applet#getCodeBase()
*/
public URL getDocumentBase()
{
return null;
}
/**
* Setup frameset. This method is called prior to the addition of any target
* areas in the framset and prior to the display of any pages. Since this
* applet does not support configurable framesets, this method ignores the
* parameter values passed.
* @param params the framset parameters if any
* <ul>
* <li>config - the layout configuration, for example <code>(COLUMN (ROW weight=1.0 left (COLUMN middleTop content middleBottom) right) bottom)</code>
* </ul>
*/
public void setupFrameset( Hashtable params )
{
String layout = (String)params.get( "config" );
if (( layout == null ) || ( layout.length() == 0 ))
layout = defaultLayoutDef;
MultiSplitLayout.Node modelRoot = MultiSplitLayout.parseModel( layout );
MultiSplitLayout multiSplitLayout = multiSplitPane.getMultiSplitLayout();
multiSplitLayout.setModel( modelRoot );
multiSplitLayout.layoutByWeight( multiSplitPane );
}
/**
* Validate and repaint the display
*/
public void refresh()
{
invalidate();
validate();
repaint();
}
public void setAppTitle( String title )
{
XTranslator translator = currentProject.getTranslator();
setTitle( translator.translate( title ));
}
/**
* Set the application icon
* @param img the image name
*/
public void setIcon( Image img )
{
setIconImage( img );
}
/**
* Setup the windowing.
* @param context the owner application context
* @param currentProject the owner project
* @param clientWidth the desired width of the application
* @param clientHeight the desired height of the application
*/
public void setupWindow( XApplicationContext context, XProject currentProject,
int clientWidth, int clientHeight )
{
currentProject.setStartupParam( "MainClass", "net.xoetrope.swing.app.XDockingApp" );
setSize( clientWidth, clientHeight );
addWindowListener( context );
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = getSize();
String center = null;
try {
center = currentProject.getStartupParam( "CenterWin" );
}
catch ( Exception ex ) {
}
if ( ( center != null ) && ( center.compareTo( "true" ) == 0 ) )
setLocation( ( screenSize.width - frameSize.width ) / 2,
( screenSize.height - frameSize.height ) / 2 );
setVisible( true );
currentProject.setApplet( null );
currentProject.setStartupObject( this );
currentProject.setAppFrame( this );
currentProject.setAppWindow( this );
}
/**
* Get the menubar, setting it up if it is not already added to the
* application frame
* @return the menu bar
*/
public Object getApplicationMenuBar()
{
if ( menuBar == null )
menuBar = getJMenuBar();
return menuBar;
}
/**
* Set the menubar
* @param mb the menubar
*/
public void setApplicationMenuBar( Object mb )
{
setJMenuBar( (JMenuBar)mb );
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI( String[] args )
{
//Make sure we have nice window decorations.... but this causes a problem
// with the Windows LAF in JDK 1.5 at least - Luan
//JFrame.setDefaultLookAndFeelDecorated( true );
//Create and set up the window.
XDockingApp frame = new XDockingApp( args );
// frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
//Display the window.
frame.setVisible( true );
}
/**
* Save the layout
*/
public void saveLayout( OutputStream os )
{
// Save the MultiSplitPane attributes
try {
XMLEncoder e = new XMLEncoder( new BufferedOutputStream( os ));
MultiSplitLayout.Node model = multiSplitPane.getMultiSplitLayout().getModel();
e.writeObject( model );
e.close();
}
catch (Exception e) {}
}
/**
* Attempt to restore the docking layout
*/
public void loadLayout( InputStream is )
{
MultiSplitLayout multiSplitLayout = multiSplitPane.getMultiSplitLayout();
try {
XMLDecoder d = new XMLDecoder(new BufferedInputStream( is ));
MultiSplitLayout.Node model = (MultiSplitLayout.Node)( d.readObject());
multiSplitLayout.setModel( model );
multiSplitLayout.setFloatingDividers( false );
d.close();
// trick is used to restore the size of the example window
multiSplitPane.setPreferredSize( model.getBounds().getSize());
dockHiddenChildren();
}
catch (Exception exc) {
MultiSplitLayout.Node model = MultiSplitLayout.parseModel( defaultLayoutDef );
multiSplitLayout.setModel( model );
}
}
/**
* Restore the normal page views, as in the case of the docking layout where
* panels may be zoomed or minimized. This method is called prior to the
* display of a new page.
*/
public void restoreViews()
{
if ( !exclusiveView )
cardPanel.restoreViews();
}
/**
* Find the dockable object that wraps a particular component
* @param content the content that is wrapped by the XDockable
* @return the XDockable instance or null if the component was not found
*/
public XDockable findDockable( Object content )
{
MultiSplitLayout multiSplitLayout = multiSplitPane.getMultiSplitLayout();
int numChildren = multiSplitPane.getComponentCount();
for ( int i = 0; i < numChildren; i++ ) {
Component comp = multiSplitPane.getComponent( i );
if ( comp instanceof XDockableFrame ) {
XDockable dockable = ((XDockableFrame)comp).findDockable( content );
if ( dockable != null )
return dockable;
}
}
XDockable dockable = cardPanel.getDockable();
if ( dockable != null ) {
if (( dockable.content == content ) || ( dockable.dockedContainer == content ) || ( dockable.header == content ))
return dockable;
}
return null;
}
/**
* Dock any children that have been restored.
*/
private void dockHiddenChildren()
{
MultiSplitLayout multiSplitLayout = multiSplitPane.getMultiSplitLayout();
int numChildren = multiSplitPane.getComponentCount();
for ( int i = 0; i < numChildren; i++ ) {
Component comp = multiSplitPane.getComponent( i );
if ( comp instanceof XDockingPanel ) {
MultiSplitLayout.Node node = multiSplitLayout.getNodeForComponent( comp );
if ( !node.isVisible())
((XDockingPanel)comp).dock();
}
}
}
}