Java 1.1 UI APIs | CodeGuru

Java 1.1 UI APIs

Bruce Eckel’s Thinking in Java Contents | Prev | Next Java 1.1 has also added some important new functionality, including focus traversal, desktop color access, printing “inside the sandbox,” and the beginnings of clipboard support. Focus traversal is quite easy, since it’s transparently present in the AWT library components and you don’t have to do […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
8 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

Java


1.1


has also added some important new functionality, including focus traversal,
desktop color access, printing “inside the sandbox,” and the
beginnings of clipboard support.
Focus
traversal is quite easy, since it’s transparently present in the AWT
library components and you don’t have to do anything to make it work. If
you make your own components and want them to handle focus traversal, you
override
isFocusTraversable( )
to return
true.
If you want to capture the keyboard focus on a mouse click, you catch the mouse
down event and call
requestFocus( ).

Desktop
colors

The


desktop
colors provide a way for you to know what the various color choices are on the
current user’s desktop. This way, you can use those colors in your
program if you desire. The colors are automatically initialized and placed in
static
members of class
SystemColor,
so all you need to do is read the member you’re interested in. The names
are intentionally self-explanatory:
desktop,
activeCaption,
activeCaptionText,
activeCaptionBorder,
inactiveCaption,
inactiveCaptionText,
inactiveCaptionBorder,
window,
windowBorder,
windowText,
menu,
menuText,
text,
textText,
textHighlight,
textHighlightText,
textInactiveText,
control,
controlText,
controlHighlight,
controlLtHighlight,
controlShadow,
controlDkShadow,
scrollbar,
info
(for
help), and
infoText
(for
help text).

Printing

Unfortunately,


there isn’t much that’s automatic with printing. Instead you must


go through a number of mechanical, non-OO steps in order to print. Printing a


component graphically can be slightly more automatic: by default, the

print( )
method calls
paint( )
to do its work. There are times when this is satisfactory, but if you want to
do anything more specialized you must know that you’re printing so you
can in particular find out the page dimensions.

The


following example demonstrates the printing of both text and graphics, and the


different approaches you can use for printing graphics. In addition, it tests


the printing support:

//: PrintDemo.java
// Printing with Java 1.1
import java.awt.*;
import java.awt.event.*;
 
public class PrintDemo extends Frame {
  Button
    printText = new Button("Print Text"),
    printGraphics = new Button("Print Graphics");
  TextField ringNum = new TextField(3);
  Choice faces = new Choice();
  Graphics g = null;
  Plot plot = new Plot3(); // Try different plots
  Toolkit tk = Toolkit.getDefaultToolkit();
  public PrintDemo() {
    ringNum.setText("3");
    ringNum.addTextListener(new RingL());
    Panel p = new Panel();
    p.setLayout(new FlowLayout());
    printText.addActionListener(new TBL());
    p.add(printText);
    p.add(new Label("Font:"));
    p.add(faces);
    printGraphics.addActionListener(new GBL());
    p.add(printGraphics);
    p.add(new Label("Rings:"));
    p.add(ringNum);
    setLayout(new BorderLayout());
    add(p, BorderLayout.NORTH);
    add(plot, BorderLayout.CENTER);
    String[] fontList = tk.getFontList();
    for(int i = 0; i < fontList.length; i++)
      faces.add(fontList[i]);
    faces.select("Serif");
  }
  class PrintData {
    public PrintJob pj;
    public int pageWidth, pageHeight;
    PrintData(String jobName) {
      pj = getToolkit().getPrintJob(
        PrintDemo.this, jobName, null);
      if(pj != null) {
        pageWidth = pj.getPageDimension().width;
        pageHeight= pj.getPageDimension().height;
        g = pj.getGraphics();
      }
    }
    void end() { pj.end(); }
  }
  class ChangeFont {
    private int stringHeight;
    ChangeFont(String face, int style,int point){
      if(g != null) {
        g.setFont(new Font(face, style, point));
        stringHeight =
          g.getFontMetrics().getHeight();
      }
    }
    int stringWidth(String s) {
      return g.getFontMetrics().stringWidth(s);
    }
    int stringHeight() { return stringHeight; }
  }
  class TBL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      PrintData pd =
        new PrintData("Print Text Test");
      // Null means print job canceled:
      if(pd == null) return;
      String s = "PrintDemo";
      ChangeFont cf = new ChangeFont(
        faces.getSelectedItem(), Font.ITALIC,72);
      g.drawString(s,
        (pd.pageWidth - cf.stringWidth(s)) / 2,
        (pd.pageHeight - cf.stringHeight()) / 3);
 
      s = "A smaller point size";
      cf = new ChangeFont(
        faces.getSelectedItem(), Font.BOLD, 48);
      g.drawString(s,
        (pd.pageWidth - cf.stringWidth(s)) / 2,
        (int)((pd.pageHeight -
           cf.stringHeight())/1.5));
      g.dispose();
      pd.end();
    }
  }
  class GBL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      PrintData pd =
        new PrintData("Print Graphics Test");
      if(pd == null) return;
      plot.print(g);
      g.dispose();
      pd.end();
    }
  }
  class RingL implements TextListener {
    public void textValueChanged(TextEvent e) {
      int i = 1;
      try {
        i = Integer.parseInt(ringNum.getText());
      } catch(NumberFormatException ex) {
        i = 1;
      }
      plot.rings = i;
      plot.repaint();
    }
  }
  public static void main(String[] args) {
    Frame pdemo = new PrintDemo();
    pdemo.setTitle("Print Demo");
    pdemo.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    pdemo.setSize(500, 500);
    pdemo.setVisible(true);
  }
}
 
class Plot extends Canvas {
  public int rings = 3;
}
 
class Plot1 extends Plot {
  // Default print() calls paint():
  public void paint(Graphics g) {
    int w = getSize().width;
    int h = getSize().height;
    int xc = w / 2;
    int yc = w / 2;
    int x = 0, y = 0;
    for(int i = 0; i < rings; i++) {
      if(x < xc && y < yc) {
        g.drawOval(x, y, w, h);
        x += 10; y += 10;
        w -= 20; h -= 20;
      }
    }
  }
}
 
class Plot2 extends Plot {
  // To fit the picture to the page, you must
  // know whether you're printing or painting:
  public void paint(Graphics g) {
    int w, h;
    if(g instanceof PrintGraphics) {
      PrintJob pj =
        ((PrintGraphics)g).getPrintJob();
      w = pj.getPageDimension().width;
      h = pj.getPageDimension().height;
    }
    else {
      w = getSize().width;
      h = getSize().height;
    }
    int xc = w / 2;
    int yc = w / 2;
    int x = 0, y = 0;
    for(int i = 0; i < rings; i++) {
      if(x < xc && y < yc) {
        g.drawOval(x, y, w, h);
        x += 10; y += 10;
        w -= 20; h -= 20;
      }
    }
  }
}
 
class Plot3 extends Plot {
  // Somewhat better. Separate 
  // printing from painting:
  public void print(Graphics g) {
    // Assume it's a PrintGraphics object:
    PrintJob pj =
      ((PrintGraphics)g).getPrintJob();
    int w = pj.getPageDimension().width;
    int h = pj.getPageDimension().height;
    doGraphics(g, w, h);
  }
  public void paint(Graphics g) {
    int w = getSize().width;
    int h = getSize().height;
    doGraphics(g, w, h);
  }
  private void doGraphics(
      Graphics g, int w, int h) {
    int xc = w / 2;
    int yc = w / 2;
    int x = 0, y = 0;
    for(int i = 0; i < rings; i++) {
      if(x < xc && y < yc) {
        g.drawOval(x, y, w, h);
        x += 10; y += 10;
        w -= 20; h -= 20;
      }
    }
  }
} ///:~ 

The


program allows you to select fonts from a


Choice

list (and you’ll see that the number of fonts available in Java 1.1


is still extremely limited, and has nothing to do with any extra fonts you
install on your machine). It uses these to print out text in bold, italic, and
in different sizes. In addition, a new type of component called a
Plot
is created to demonstrate graphics. A
Plot
has rings that it will display on the screen and print onto paper, and the
three derived classes
Plot1,
Plot2,
and
Plot3
perform these tasks in different ways so that you can see your alternatives
when printing graphics. Also, you can change the number of rings in a plot
– this is interesting because it shows the printing fragility in Java 1.1
.
On my system, the printer gave error messages and didn’t print correctly
when the ring count got “too high” (whatever that means), but
worked fine when the count was “low enough.” You will notice, too,
that the page dimensions produced when printing do not seem to correspond to
the actual dimensions of the page. This might be fixed in a future release of
Java, and you can use this program to test it.

This


program encapsulates functionality inside inner classes whenever possible, to


facilitate reuse. For example, whenever you want to begin a print job (whether


for graphics or text), you must create a

PrintJob
object, which has its own
Graphics
object
along with the width and height of the page. The creation of a
PrintJob
and extraction of page dimensions is encapsulated in the
PrintData
class.


Printing
text

Conceptually,


printing
text is straightforward: you choose a typeface and size, decide where the
string should go on the page, and draw it with
Graphics.drawString( ).
This means, however, that you must perform the calculations of exactly where
each line will go on the page to make sure it doesn’t run off the end of
the page or collide with other lines. If you want to make a word processor,
your work is cut out for you.
ChangeFont

encapsulates a little of the process of changing from one font to another by


automatically creating a new

Font
object with your desired typeface, style (
Font.BOLD
or
Font.ITALIC
– there’s no support for underline, strikethrough, etc.), and point
size. It also simplifies the calculation of the width and height of a string.

When


you press the “Print text” button, the


TBL

listener


is activated. You can see that it goes through two iterations of creating a


ChangeFont

object and calling


drawString( )

to print out the string in a calculated position, centered, one-third, and


two-thirds down the page, respectively. Notice whether these calculations


produce the expected results. (They didn’t with the version I used.)


Printing
graphics

When


you press the “Print graphics” button the


GBL

listener is activated. The creation of a


PrintData

object initializes


g

,


and then you simply call


print( )

for the component you want to print. To force printing you must call

dispose( )
for the
Graphics
object and
end( )
for the
PrintData
object (which turns around and calls
end( )
for the
PrintJob).

The


work is going on inside the


Plot

object. You can see that the base-class


Plot

is simple – it extends


Canvas

and contains an


int

called


rings

to indicate how many concentric rings to draw on this particular


Canvas

.


The three derived classes show different approaches to accomplishing the same


goal: drawing on both the screen and on the printed page.

Plot1

takes


the simplest approach to coding: ignore the fact that there are differences in


painting and printing, and just override

paint( ).
The reason this works is that the default

print( )
method simply turns around and calls
paint( ).
However, you’ll notice that the size of the output depends on the size of
the on-screen canvas, which makes sense since the
width
and
height
are determined by calling
Canvas.getSize( ).
The other situation in which this is acceptable is if your image is always a
fixed size.

When


the size of the drawing surface is important, then you must discover the


dimensions. Unfortunately, this turns out to be awkward, as you can see in


Plot2

.


For some possibly good reason that I don’t know, you cannot simply ask the


Graphics

object the dimensions of its drawing surface. This would have made the whole


process quite elegant. Instead, to see if you’re printing rather than


painting, you must detect the

PrintGraphics
using the RTTI
instanceof
keyword (described in Chapter 11), then downcast and call the sole
PrintGraphics
method:
getPrintJob( ).
Now you have a handle to the
PrintJob
and you can find out the width and height of the paper. This is a hacky
approach, but perhaps there is some rational reason for it. (On the other hand,
you’ve seen some of the other library designs by now so you might get the
impression that the designers were, in fact, just hacking around…)

You


can see that


paint( )

in


Plot2

goes


through both possibilities of printing or painting. But since the


print( )

method should be called when printing, why not use that? This approach is used


in


Plot3

,


and it eliminates the need to use


instanceof

since inside


print( )

you can assume that you can cast to a


PrintGraphics

object. This is a little better. The situation is improved by placing the


common drawing code (once the dimensions have been detected) inside a separate


method


doGraphics( )

.


Running
Frames within applets

What


if you’d like to print from within an applet? Well, to print anything you


must get a


PrintJob

object through a

Toolkit
object’s
getPrintJob( )
method, which takes only a
Frame
object and not an
Applet.
Thus it would seem that it’s possible to print from within an
application, but not an applet. However, it turns out that you can
create
a
Frame
from within an applet (which is the reverse of what I’ve been doing for
the applet/application examples so far, which has been making an applet and
putting inside a
Frame).
This is a useful technique since it allows you to use many
applications
within applets (as long as they don’t violate applet security). When the
application window comes up within an applet, however, you’ll notice that
the Web browser sticks a little caveat on it, something to the effect of
“Warning: Applet Window.”

You


can see that it’s quite straightforward to put a


Frame

inside an applet. The only thing that you must add is code to

dispose( )
of the
Frame
when the user closes it (instead of calling
System.exit( )):
//: PrintDemoApplet.java
// Creating a Frame from within an Applet
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
 
public class PrintDemoApplet extends Applet {
  public void init() {
    Button b = new Button("Run PrintDemo");
    b.addActionListener(new PDL());
    add(b);
  }
  class PDL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      final PrintDemo pd = new PrintDemo();
      pd.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e){
          pd.dispose();
        }
      });
      pd.setSize(500, 500);
      pd.show();
    }
  }
} ///:~ 

There’s


some confusion involved with Java 1.1


printing
support. Some of the publicity seemed to claim that you’d be able to
print from within an applet. However, the Java security system contains a
feature that could lock out an applet from initiating its own print job,
requiring that the initiation be done via a Web browser or applet viewer. At
the time of this writing, this seemed to remain an unresolved issue. When I ran
this program from within a Web browser, the
PrintDemo
window came up just fine, but it wouldn’t print from the browser.

Advertisement

The
clipboard

Java


1.1


supports limited operations with the
system
clipboard (in the
java.awt.datatransfer
package).
You can copy
String
objects to the clipboard as text, and you can paste text from the clipboard into
String
objects. Of course, the clipboard is designed to hold any type of data, but how
this data is represented on the clipboard is up to the program doing the
cutting and pasting. Although it currently supports only string data, the Java
clipboard API provides for extensibility through the concept of a
“flavor.” When data comes off the clipboard, it has an associated
set of
flavors
that it can be converted to (for example, a graph might be represented as a
string of numbers or as an image) and you can see if that particular clipboard
data supports the flavor you’re interested in.

The


following program is a simple demonstration of cut, copy, and paste with


String

data in a

TextArea.
One thing you’ll notice is that the keyboard sequences you normally use
for cutting, copying, and pasting also work. But if you look at any
TextField
or
TextArea
in any other program you’ll find that they also automatically support the
clipboard key sequences. This example simply adds programmatic control of the
clipboard, and you could use these techniques if you want to capture clipboard
text into some non-
TextComponent.
//: CutAndPaste.java
// Using the clipboard from Java 1.1
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
 
public class CutAndPaste extends Frame {
  MenuBar mb = new MenuBar();
  Menu edit = new Menu("Edit");
  MenuItem
    cut = new MenuItem("Cut"),
    copy = new MenuItem("Copy"),
    paste = new MenuItem("Paste");
  TextArea text = new TextArea(20,20);
  Clipboard clipbd =
    getToolkit().getSystemClipboard();
  public CutAndPaste() {
    cut.addActionListener(new CutL());
    copy.addActionListener(new CopyL());
    paste.addActionListener(new PasteL());
    edit.add(cut);
    edit.add(copy);
    edit.add(paste);
    mb.add(edit);
    setMenuBar(mb);
    add(text, BorderLayout.CENTER);
  }
  class CopyL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      String selection = text.getSelectedText();
      StringSelection clipString =
        new StringSelection(selection);
      clipbd.setContents(clipString, clipString);
    }
  }
  class CutL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      String selection = text.getSelectedText();
      StringSelection clipString =
        new StringSelection(selection);
      clipbd.setContents(clipString, clipString);
      text.replaceRange("",
        text.getSelectionStart(),
        text.getSelectionEnd());
    }
  }
  class PasteL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      Transferable clipData =
        clipbd.getContents(CutAndPaste.this);
      try {
        String clipString =
          (String)clipData.
            getTransferData(
              DataFlavor.stringFlavor);
        text.replaceRange(clipString,
          text.getSelectionStart(),
          text.getSelectionEnd());
      } catch(Exception ex) {
        System.out.println("not String flavor");
      }
    }
  }
  public static void main(String[] args) {
    CutAndPaste cp = new CutAndPaste();
    cp.addWindowListener(
      new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          System.exit(0);
        }
      });
    cp.setSize(300,200);
    cp.setVisible(true);
  }
} ///:~ 

The


creation and addition of the menu and


TextArea

should by now seem a pedestrian activity. What’s different is the


creation of the


Clipboard

field


clipbd

,


which is done through the


Toolkit

.

All


the action takes place in the listeners. The


CopyL

and


CutL

listeners are the same except for the last line of


CutL

,


which erases the line that’s been copied. The special two lines are the


creation of a

StringSelection
object from the
String
and the call to
setContents( )
with this
StringSelection.
That’s all there is to putting a
String
on the clipboard.

In


PasteL,

data is pulled off the clipboard using

getContents( ).
What comes back is a fairly anonymous
Transferable
object, and you don’t really know what it contains. One way to find out
is to call
getTransferDataFlavors( ),
which returns an array of
DataFlavor
objects indicating which flavors are supported by this particular object. You
can also ask it directly with
isDataFlavorSupported( ),
passing in the flavor you’re interested in. Here, however, the bold
approach is taken:
getTransferData( )
is called assuming that the contents supports the
String
flavor, and if it doesn’t the problem is sorted out in the exception
handler.

In


the future you can expect more data flavors to be supported.


Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.