[an error occurred while processing this directive]
The Morgue Commentary PLAF Papers Friends Tech Topics Swing Text The Web Tech Topics What's Swing?
Index Archive Call 911 PLAF Papers Friends Tech Topics Swing Text Swing & the Web IDE Roundup Special Report Databank Page 2 Page One What's Swing?
JDK Docs Download JDK Swing API Docs Download Swing Java Tutorial
The Swing Connection The Archive Friends

Creating MDI Apps With Swing

With AWT, You Couldn't Do It; with Swing, You Can

By Ethan Henry
Java Evangelist, KL Group

Picture of an open windowEver since the advent of Microsoft Windows 3.0 and the X Windows UNIX environment, the MDI (Multiple Document Interface) model has been the predominant format for organizing and presenting information in windows-style environments. Open almost any currently available Windows application (such as Word, Access, or Excel), and you'll find that it's an MDI application. Many X Windows and Motif applications are also MDI programs.

In a nutshell, MDI is a graphical user interface (GUI) mechanism that makes it possible to arrange multiple child windows inside a parent window. (An alternative user-interface model, called SDI -- or single-document interface -- lets an application display only one main window at a time).

In an MDI environment, child windows are totally contained a their parent window – in effect, the parent window becomes a new desktop within which the children operate. When you minimize a child window, it is iconified at the bottom of the parent window. When you move a child window, it is clipped by the boundaries of the parent window. And when you minimize a parent window, only one entry appears in the window list maintained by your window manager.

In pre-Swing versions of the Advanced Windowing Toolkit (AWT), there was no way to build MDI applications. You could create multiple Frame objects, but each Frame was a top-level window, and was completely independent. You could create a MDI-like application that created a Frame for each window, but then the user’s screen quickly got cluttered -- and if the user wanted to minimize the entire application to work on something else, the user had to minimize each Frame. Forthermore, each individual Frame created an icon on the desktop when it was minimized. Things got messy pretty quickly.

Swing eliminates all these problems by simply supporting the MDI user interface. Finally, Swing allows Java developers to create real MDI applications by providing a class named JInternalFrame. In Swing, JInternalFrame objects are child windows held inside a special container called a JDesktopPane. And a JDesktopPane object can turn any container, like a JFrame or JApplet, into a parent window.

Horizontal rule

Swing offers a solution

This article explains how Swing's JDesktopPane and JDesktopPane classes work, and shows how you can use them to create MDI-style applications using the Java programming language. Major topics covered in this article are:

Horizontal rule

Note iconFor More Information . . .

If you aren't familiar with how container classes work in Swing, it might be helpful to read the archived article titled "Understanding Containers" before you continue reading this article.

Horizontal rule

The JInternalFrame class

JInternalFrame is extremely useful, but this usefulness doesn’t come without a price – you can’t just drop a JInternalFrame into any old container. You need a special container that understands how to manage JInternalFrame objects – the JDesktopPane. So, in order to use JInternalFrame, we have to know how to use JDesktopPane. Before we can do that, however, we have to understand yet another class – JLayeredPane. JDesktopPane derives from JLayeredPane and most of the JDesktopPane functionality is inherited from it. So, on to JLayeredPane.


How JLayeredPane works

Architecturally, JLayeredPane is a container that manages components in layers, so components can overlap and still be managed and organized. Components in higher layers are displayed above components in lower layers. Each layer is identified by an Integer, but to make things easier to deal with, JLayeredPane uses predefined constants to specific five specific layers, each one above the preceding ones. These five predefined constants are:

  • DEFAULT_LAYER: The bottommost layer, where most components go.
  • PALETTE_LAYER: Sits above the default layer. This layer is useful for implementing components such as floating toolbars and palettes. It makes them appear above other components.
  • MODAL_LAYER: The layer used for model dialogs.
  • POPUP_LAYER: The layer where popup windows appear. Putting popup windows associated with combo boxes, tooltips and other help text in this layer ensures that they appear above whatever component generated them.
  • DRAG_LAYER: When you want to drage a component, move it to the drag layer. Then, while it is moving, it appears above all other components. When you have finished dragging the component, return the component to its previous layer.

Adding components

Although there are exactly five predefined layers, there is plenty of space between these values -- enough space for you to fine-tune exactly where the components in your program will appear. To add components to a JLayeredPane, you use the add()method, just as you would with any other container:

JFrame frame = new JFrame(); JLayeredPane layeredPane = frame.getLayeredPane(); JComponent child = [some component]; layeredPane.add(child);
Using the standard (one-parameter) version of the add() method adds a component to the default layer. It is equivalent to:
layeredPane.add(child, JLayeredPane.DEFAULT_LAYER);

Once you have placed a component inside a JLayeredPane, you can change its position by calling setLayer():

layeredPane.setLayer(child, JLayeredPane.DRAG_LAYER); // move the child around layeredPane.setLayer(child, JLayeredPane.DEFAULT_LAYER);

Stacking components

You can stack components inside each layer of a layered pane in the same way you would stack them in a standard AWT container -- you just give each component a position number in the layer in which it appears. The position numbers in a given layer range from 0 to 1 less than the number of components in the layer. The topmost component has position 0, and a value of –1 indicates the bottommost component. (You may notice that this arrangement is just the opposite from the way that layers stack -- when you stack layers, the bottommost component is at position 0). To set the position of a component inside a layer when it is added, there is a third variant of add():

   // add the component to the top of the default layer
   layeredPane.add(child, JLayeredPane.DEFAULT_LAYER, 0);
To move components within the layer in which they reside, you can use the convenience methods moveToFront() and moveToBack() on JLayeredPane, to move components up and down within their own layer:
// move the component to the front layeredPane.moveToFront(child); // move it to the back layeredPane.moveToBack(child);
Note that a call to moveToFront() may not put the component above all other components in the JLayeredPane - it only puts it in front of other components in the same layer – components in higher layers will still appear in front of components in lower layers. The same is true of moveToback().

One important thing to note about JLayeredPane is that by default, it does not have a layout manager set and you shouldn’t set one, as using a layout manager will prevent component from overlapping, which is why you’re using JLayeredPane in the first place.

JLayeredPane objects are also used by JRootPane – every root pane contains a layered pane. The root pane’s content pane, which is the container that is most useful, is a JPanel that’s been added to the layered pane below the default layer, so that it’s automatically underneath anything else you add to the layered pane.

Now that we’ve gone through all the work of explaining how JLayeredPane works, the truth is that most developers will never use it directly. If you’re dealing with JRootPane and you don’t need to layer components or use JInternalFrame, you’ll probably just skip the JLayeredPane and go directly to the root pane’s content pane. If you do want to use internal frames, then you’re not going to be using JInternalFrame, you’re going to be using JDesktopPane.

Horizontal rule

The JDesktopPane class

JDesktopPane, a class derived from JLayeredPane, does the same basic thing as JLayeredPane does – it manages overlapping components. The most important difference between the two classes is that JDesktopPane is specially designed to manage just one specific type of component – JInternalFrame.

To do this job, JDesktopPane provides two extra methods that can come in hand when you need to manage JInternalFrames. These two methods are getAllFrames() and getAllFramesInLayer(). Here's how you use them:

JDesktopPane desktop = [some desktop pane] JInternalFrame frames[]; frames = desktop.getAllFrames(); // or frames = desktop.getAllFramesInLayer (JDesktopPane.DEFAULT_LAYER);
JDesktopPane, unlike JLayeredPane, has a UI class associated with it in the Swing's pluggable-look-and-feel (plaf) package. Consequently, its appearance and behavior are subject to change, depending on which look and feel you select. While most of the PL&Fs supplied with Swing don’t change the appearance of the desktop pane, the desktop pane does get one important thing from its UI class: its DesktopManager.

Horizontal rule

The DesktopManager

To be precise, DesktopManager is an interface, not a class. Classes that implement DesktopManager provide a set of methods that allow a desktop pane to handle the various kinds of operations that can be performed on an internal frame -- operations such as opening, closing, dragging, resizing, iconifying, minimizing and maximizing. For example, if the desktop manager wants to show a frame that's being dragged using only the frame’s outline instead of dragging the entire frame, it might override these methods from DesktopManager:

   public void beginDraggingFrame(JComponent f);
   public void dragFrame(JComponent f, int x, int y);
   public void endDraggingFrame(JComponent f);
Some of Swing's look-and-feel packages (such as the Windows PL&F design) implement their own DesktopManager classes. And there is a DefaultDesktopManager class in the main Swing package which will be used if no other DesktopManager is available.

If you want to override the default behavior of the actions listed above on individual internal frames, you can register listeners on the individual internal frame objects. To change the behavior of all the frames on a desktop, it's much easier to subclass one of the existing DesktopManager classes and use it instead of using the JDesktopPane’s default desktop manager. For example:

   public class MyDesktopManager 
                 extends DefaultDesktopManager {
       public void activateFrame(JInternalFrame f) {
           System.out.println
                 ("Frame "+f+" obtained the focus");
       }
   }

   // inside some other class
   public JDesktopPane createDesktop() {
       JDesktopPane desktop = new JDesktopPane();
       desktop.setDesktopManager(new MyDesktopManager());
       Return desktop;
   }

Using JInternalFrame

In Swing, an internal frame is a lightweight object that provides many of the features of a native frame, including dragging, closing, becoming an icon, resizing, title display, and support for a menu bar. Generally, you create an internal frame by instantiating a JInternalFrame object and then adding it to a JDesktopPane. Actions specific to the look and feel that is being used are then delegated to the DesktopManager object maintained by the JDesktopPane (as set by the UI).

Now that we’re familiar with all the classes that interact with the JInternalFrameclass, we’re ready to create a sample application that uses internal frames. JInternalFrame is a sample program that illustrates:

  • Creating a custom DesktopManager class.
  • Using an anonymous inner class.
  • Creating a number of JInternalFrames in a JDesktopPane in different layers.

Example program: JInternalFrame

import com.sun.java.swing.*;
import java.awt.BorderLayout;

public class InternalFrames extends JFrame {

   JDesktopPane desktop;
   DesktopManager manager;

   public InternalFrames() {
      super("Internal Frames Demo");
      top = new JDesktopPane();
      desktop.setOpaque(false);
      getContentPane().add(desktop,BorderLayout.CENTER);

      manager = new DefaultDesktopManager() {
           public void activateFrame(JInternalFrame f) {
           super.activateFrame(f);
           System.out.println(f);
         }
      };
    
      desktop.setDesktopManager(manager);

      JInternalFrame internal;
      JButton button;

      internal = new JInternalFrame("Always Below",
           true,false,true,true);
      button = new JButton("Ok");
      internal.getContentPane().add(button,BorderLayout.CENTER);
      internal.setBounds(0,0,200,75);
      desktop.add(internal,
           new Integer(desktop.DEFAULT_LAYER.intValue()-1));

      internal = new JInternalFrame("Default Layer #1",
           true,false,true,true);
      button = new JButton("Ok");
      internal.getContentPane().add(button,BorderLayout.CENTER);
      internal.setBounds(25,25,200,75);
      desktop.add(internal,desktop.DEFAULT_LAYER);

      internal = new JInternalFrame("Default Layer #2",
           true,false,true,true);
      button = new JButton("Ok");
      internal.getContentPane().add(button,BorderLayout.CENTER);
      internal.setBounds(50,50,200,75);
      desktop.add(internal,desktop.DEFAULT_LAYER);

      internal = new JInternalFrame("Always Above",
           true,false,true,true);
      button = new JButton("Ok");
      internal.getContentPane().add(button,BorderLayout.CENTER);
      internal.setBounds(75,75,200,75);
      desktop.add(internal,
           new Integer(desktop.DEFAULT_LAYER.intValue()+1));

      setSize(300,300);
      show();
   }

   public static void main(String args[]) {
       new InternalFrames();
   }
}

Horizontal rule

Conclusion

The JInternalPane class adds great new functionality to GUIs written in Java with Swing. Although creating and using JInternalPane might seem complicated at first glance, once you understand all the parts involved, they’re as easy to use as they are elegant.

 

[an error occurred while processing this directive]