12. Displaying text

Sometimes, you may want to show a table or a number of text lines in an applet.

Suppose you want to show the days of the week in the form of a list. That could be done quite simply in the following way:

/* simple program to show
   the days of the week */

import java.applet.*;
import java.awt.*;

public class SimpleDays extends Applet {

     public void init ( ) {
          setBackground (Color.gray);
     }

     public void paint (Graphics g) {
          Font  large = new Font ("Arial", Font.BOLD, 20);
          g.setFont (large);
          g.setColor (Color.yellow);
          g.drawString ("Monday", 50, 40); 
          g.drawString ("Tuesday", 50, 70); 
          g.drawString ("Wednesday", 50, 100); 
          g.drawString ("Thursday", 50, 130); 
          g.drawString ("Friday", 50, 160); 
          g.drawString ("Saturday", 50, 190); 
          g.drawString ("Sunday", 50, 220); 
     }
}

The program is not very complicated. It starts with comment -- which is marked by /* and */:

/* simple program to show
   the days of the week */

In the method paint, the words Monday through Sunday are shown in yellow letters on a gray background (which we turned on in init), like this:

      




More efficient data handling

The days-of-the-week program does work, but it's not very efficient.

Suppose we don't want to show seven but hundreds of data, we should really organize our program differently.

It would be more efficient to put the days of the week in an array. In that case the method
paint would start looking like this:

public void paint  (Graphics g) {
     String days[ ] =  {"Mon", "Tues", "Wednes",
          "Thurs", "Fri", "Satur", "Sun"};
     Font large = new Font ("Arial", Font.BOLD, 20);
     g.setFont  (large);
     g.setColor (Color.yellow);
     for (int  i=0;  i<7;  i++)
          g.drawString (days[i] + "day",  50,  40 + i * 30);
}
The way in which the days are displayed works like this: we use a for-lus to assign the values 0 through 6 to the variable i.

The value of days[0] is "Mon", days[1] is "Tues", and so on to days[6], which has the value "Sun".

Each of the days contains the part "day". We don't store this part seven times, but prefer to "stick" it on to the string by the time it's going to be displayed.

The program does not contain a lot of data. Still, we've managed to make it shorter than the first version. It's clear that the more data it contains, the more program text we can save in this way.

But this program can be improved in more ways. But first something completely different ...


Font size

Generally, there are two ways in which we can make a button. The first was discussed in chapter 6, the prefab Java-Button:



The second way is by clicking certain parts of the applet window and make the program interpret this in a certain way, for instance like this:
// a Button that can be 'read' with mouseListener
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class AboveOrBelow extends Applet
                                  implements MouseListener {
   int clickY=9999;

   public void init () {
      setBackground (Color.gray);
      addMouseListener (this);
   }

   public void paint (Graphics g) {
      Dimension d = getSize();
      int height = d.height, width=d.width;
      g.setColor (Color.yellow);
      g.setFont (new Font ("Verdana",Font.BOLD, 16));
      g.drawLine(0,height/2,width,height/2);
      String tl = " de lijn.";
      if (clickY==9999)
         g.drawString ("click above or below "+tl,20,height/4);
      else if (clickY<height/2)
         g.drawString ("Clicked above"+tl,20,height/4);
      else if (clickY<height/2)
         g.drawString ("Clicked below"+tl,20,3*height/4);
      else
         g.drawString ("Clicked on"+tl,20,height/2);
   }

    public void mousePressed (MouseEvent evt) {
         clickY = evt.getY();
         repaint();
   }

   public void mouseReleased (MouseEvent evt){}
   public void mouseEntered (MouseEvent evt){}
   public void mouseExited (MouseEvent evt){}
   public void mouseClicked (MouseEvent evt){}
}
In paint, the window is divided by a straight line into a lower and a higher part.

Once the window is clicked, the method evt.getY in mousePressed will capture the y-coordinate, which is assigned to the global variable clickY. Thus in paint it can be determined whether the click took place above or below the line. The working program looks like this:



Often you will want to put a text on such a button. It goes without saying that such a text should fit nicely -- so not anything like these here:



It will usually look best if the text covers the greater part of the button width. 

We can write a method that will display such a button. This method should have five arguments
  • x-coordinate
  • y-coordinate
  • width
  • height
  • text
Apart from the method buttonClicked, a second method would come in handy (buttonClicked), which can be used to determine whether the button has actually been clicked:
// method to make a button with text
// and how to display centered text
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class ButtonWithText extends Applet
                              implements MouseListener {
   int clickX, clickY, winWi, winHi; // vensterbreedte, -hoogte
   boolean putText = false;

   public void init () {
      setBackground (Color.pink);
      addMouseListener (this);
   }

   void makeButton (int x, int y, int wi, int hi, String s) {
      int fg=1, len;
      FontMetrics fm;
      Graphics g = getGraphics();
      g.setColor (Color.gray);
      g.fillRect (x+3, y+3, wi, hi);
      g.setColor (Color.red);
      g.fillRect (x-3, y-3, wi, hi);
      g.setColor (Color.black);
      g.drawRect (x-3, y-3, wi, hi);
      do {
         g.setFont (new Font ("Verdana",Font.BOLD, fg++));
      } while (g.getFontMetrics().stringWidth(s)<wi-14);
      fg--;
      g.setFont (new Font ("Verdana",Font.BOLD, fg));
      g.setColor (Color.yellow);
      len = g.getFontMetrics().stringWidth(s);
      g.drawString (s, x-3+wi/2-len/2, y-3+hi/2+fg/2);
   }

   boolean buttonClicked (int x, int y, int wi, int hi) {
      if (clickX>x-3 && clickY<x-3+wi
                  && clickY>y-3 && clickY<y+hi)
         return true;
      else
         return false;
   }

   public void paint (Graphics g) {
      Dimension d = getSize();
      winHi = d.height; winWi=d.width;
      int strW;
      makeButton (winWi/4, winHi/4, winWi/2, winHi/3, "click here");
      String txt[ ] = {"Demo of stringWidth", "(class: FontMetrics)"};
      if (buttonClicked (winWi/4, winHi/4, winWi/2, winHi/3)
                  && putText) {
         for (int i=0; i<2; i++) {
            g.setColor (Color.black);
            g.setFont (new Font ("Verdana",Font.BOLD, 20));
            strW = g.getFontMetrics().stringWidth(txt[i]);
            g.drawString (txt[i], winWi/2-strW/2, (10+i*2)*winHi/13);
         }
      }
   }

   public void mousePressed (MouseEvent evt) {
      clickX = evt.getX();
      clickY = evt.getY();
      putText = !putText;
      repaint();
   }

   public void mouseReleased (MouseEvent evt){}
   public void mouseEntered (MouseEvent evt){}
   public void mouseExited (MouseEvent evt){}
   public void mouseClicked (MouseEvent evt){}
}
Once the mouse has be
en clicked, the program will go to mousePressed, where the x and y coordinates will be assigned to the (globally declared) clickX and clickY

The boolean variable putText will assume its opposite value (the exclamation mark means: not). In other words, if it is true, it will become false, and vice versa.

When the program encounters repaint() , it will go to paint, where a new button is painted by makeButton and buttonClicked will check whether the area of the button has been clicked.

Finally if
putText equals true, two new texts will be painted.



"Packing" a string-array 

We return to our days-of-the-week program, which can be improved in at least one more way. The data in the following program line consist of 51 characters, 22 of which are commas, quotes and curly braces. That's over 40% of the characters typed.

     String days[ ] =  {"Mon","Tues","Wednes","Thurs","Fri","Satur","Sun"};

We could "pack" the days into a string of which the words are separated by just one character, for instance like this:

     String s = "Mon-Tues-Wednes-Thurs-Fri-Satur-Sun";

It's possible to "unpack" this string to an array. For this end we have the class StringTokenizer.

In order to use this class, we first have to insert the following line into the beginning of our applet program:

     import java.util.StringTokenizer;

First we have to define an object (in this case: st) of this class, which needs two arguments: the name of the string to be unpacked and the separator character (hyphen in this case).

     StringTokenizer st = new StringTokenizer (s, "-");

The elements are added to the array in the following way:

     String days[ ] = new String [7];
     int counter = 0;
     while (st.hasMoreTokens( ))  {
           days[counter++] = st.nextToken( );
     }


Displaying a whole text

As we have seen above, the method stringWidth can be used to get text to fit into a certain space.

If you have to display a larger amount of text, it's useful to decide word by word whether the text still fits into the available space.

We are going to do this in the last program of this chapter, in which text consisting of a few hundred words is displayed inside the applet window:

// Demonstration of the class StringTokenizer
import java.util.StringTokenizer;
import java.applet.*;
import java.awt.*;
public class DisplayingText extends Applet {

   public void init () {
      setBackground (Color.gray);
   }

   public void paint (Graphics g) {
      String s = "Of course, the machine language program (the "+
        "executable) depends on the kind of system that we want "+
        "to run it on: the program contains instructions to one "+
        "specific processor and calls to one specific operating "+
        "system. Such programs usually run on just one system; a "+
        "program that runs well on Windows is useless on other sy"+
        "stems, such as Unix or MacOS. It wouldn't make much sens"+
        "e either to have these kinds of programs executed on the"+
        " Internet. After all, the Internet is populated by all "+
        "kinds of computer systems. If you would like to use such "+
        "a (conventional) program to enliven your homepage, you "+
        "would have to upload versions for Linux, Windows and va"+
        "rious other systems. There is, however, another problem: "+
        "Internet users can't risk downloading unknown programs "+
        "and then allow them to run on their system. Who, for ins"+
        "tance, will guarantee that a program downloaded from the "+
        "Internet will not start formatting the hard-disk. To bot"+
        "h problems Java offers a solution.";
      String words[] = new String[500];
      StringTokenizer st = new StringTokenizer (s," ");
      int number = 0;
      while (st.hasMoreTokens()) {                   // begin
         words[number++] = st.nextToken();
      }
      Dimension d = getSize();
      int width = d.width, i = 0, height = 18; 
      g.setColor (Color.yellow);
      g.setFont (new Font ("Arial", Font.PLAIN, 18));
      String line = "";
      FontMetrics fm = g.getFontMetrics();
      while (i < number) {
         if (fm.stringWidth(line+" "+words[i]) < width-width/10) {
            line += " " + words[i];
            i++;
         }
         else {
            g.drawString (line, width/20, height+=25);
            line = "";
         }
      }
      g.drawString (line, width/20, height+=25);     // end
   }
}

There is no technical objection why the string s could not be written as one long line. But this might get outside the boundaries of a program editor, which might be somewhat awkward.

The actual process of displaying text takes place between the lines marked by //begin and //end. Repeatedly, checks are made whether a line including the next word still fits into 9/10 of the width of the window.  

If it does, we add this word to line.

But if it doesn't fit, we display the line (without the word checked last) , we increase the height for the next line, after which we start with an empty line.

What this looks like in practice, can be seen here:




Of course, it's also possible to align text on both sides. A program that does that may be a little too complicated for this tutorial. But for those who are interested, I'll give the program here anyway:

// Demonstration of the class StringTokenizer
// and right and left side aligning of text

import java.applet.*;
import java.awt.*;
import java.util.StringTokenizer;
public class DisplayingText2 extends Applet {
   
   public void init () {
      setBackground (Color.gray);
   }
   
   public void paint (Graphics g) {
      String s = "Of course, the machine language program (the "+
        "executable) depends on the kind of system that we want "+
        "to run it on: the program contains instructions to one "+
        "specific processor and calls to one specific operating "+
        "system. Such programs usually run on just one system; a "+
        "program that runs well on Windows is useless on other sy"+
        "stems, such as Unix or MacOS. It wouldn't make much sens"+
        "e either to have these kinds of programs executed on the"+
        " Internet. After all, the Internet is populated by all "+
        "kinds of computer systems. If you would like to use such "+
        "a (conventional) program to enliven your homepage, you "+
        "would have to upload versions for Linux, Windows and va"+
        "rious other systems. There is, however, another problem: "+
        "Internet users can't risk downloading unknown programs "+
        "and then allow them to run on their system. Who, for ins"+
        "tance, will guarantee that a program downloaded from the "+
        "Internet will not start formatting the hard-disk. To bot"+
        "h problems Java offers a solution.";
      String words[] = new String[200];
      StringTokenizer st = new StringTokenizer (s," ");
      int number = 0;
      while (st.hasMoreTokens()) {
         words[number++] = st.nextToken();
      }
      Dimension d = getSize();
      int width = d.width, height = 30;
      g.setColor (Color.yellow);
      g.setFont (new Font ("Arial", Font.PLAIN, 18));
      String line = words[0];
      int first=0, last=0, w, pos[] = new int[20], p=1;
      pos[0] = 0;
      int space = 9*width/10;
      FontMetrics fm = g.getFontMetrics();
      int index = 1;
      while (index < number) {
         if (fm.stringWidth(line+" "+words[index]) <= space) {
            pos[p++] = fm.stringWidth(line+" ");
            line += " " + words[index];
            last = index;
            index++;
         }
         else {
            int begin = 1;
            for (int gap=space-fm.stringWidth(line); gap>0; gap--) {
               if (pos[begin]==0)
                  begin = 1;
               for (w=begin; pos[w]>0; w++)
                  pos[w]++;
               begin++;
            }
            p = 0;
            for (w=first; w<=last; w++) {
               g.drawString (words[w], width/20+pos[p], height);
               p++;
            }
            line = words[index];
            first = index;
            index++;
            for (p=0; p<pos.length; p++)
               pos[p]=0;
            height+=25;
            p = 1;
         }
      }
      g.drawString (line, width/20, height);
   }
}
Here too, I'll show you what it looks like:




Exercise 12.1
Write a program that displays a grey rectangle on a black background in the center of the screen. In it, your name in yellow letters should be displayed, which fits exactly oin the right and left. The width of your name should be between 40 and 50 percent of the applet window (in any browser) for instance like this:



Exercise 12.2
Copyright-notices are often found in small lettering in the bottom right hand corner. Expand the previous program so that a copyright notice of about 140 by 160 pixels width comes into the bottom right hand corner, for instance like this:

 

Exercise 12.3
Rewrite exercise 12.2 so that the font sizes of the large text and the small one are determined in a method which finds out the maximum font size which allows a certain text still fits into a certain available space, and which may look like this:

     int fixFontSize (Graphics g, String text, int space)


Chapter 14

Menu Java Tutorial

home page

(c) 2003, Thomas J.H. Luif