Java 1.1 UI APIs

Bruce Eckel’s Thinking in Java Contents | Prev | Next

Desktop
colors

Printing

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.


Printing
text

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

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.

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.”

//: 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();
    }
  }
} ///:~ 

The
clipboard

//: 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.

More by Author

Must Read