FileDocCategorySizeDatePackage
MultiLineLabel.javaAPI DocExample5874Sat Jan 24 10:44:38 GMT 2004je3.beans

MultiLineLabel.java

/*
 * Copyright (c) 2004 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 3nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose,
 * including teaching and use in open-source projects.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book, 
 * please visit http://www.davidflanagan.com/javaexamples3.
 */
package je3.beans;
import java.awt.*;
import javax.swing.*;
import java.util.StringTokenizer;

/**
 * A custom component that displays multiple lines of text with specified
 * margins and alignment.
 **/
public class MultiLineLabel extends JComponent {
    // User-specified properties
    protected String label;             // The label, not broken into lines
    protected int margin_width;         // Left and right margins
    protected int margin_height;        // Top and bottom margins
    protected Alignment alignment;      // The alignment of the text.

    // Computed state values
    protected int num_lines;            // The number of lines
    protected String[] lines;           // The label, broken into lines
    protected int[] line_widths;        // How wide each line is
    protected int max_width;            // The width of the widest line
    protected int line_height;          // Total height of the font
    protected int line_ascent;          // Font height above baseline
    protected boolean measured = false; // Have the lines been measured?
    
    // Here are five versions of the constructor.
    public MultiLineLabel(String label, int margin_width,
			  int margin_height, Alignment alignment) {
	this.label = label;                 // Remember all the properties.
	this.margin_width = margin_width;
	this.margin_height = margin_height;
	this.alignment = alignment;
	newLabel();                         // Break the label up into lines.
    }

    public MultiLineLabel(String label, int margin_width, int margin_height) {
	this(label, margin_width, margin_height, Alignment.LEFT);
    }

    public MultiLineLabel(String label, Alignment alignment) {
	this(label, 10, 10, alignment);
    }

    public MultiLineLabel(String label) { this(label, 10, 10, Alignment.LEFT);}

    public MultiLineLabel() { this(""); }
    
    // Methods to set and query the various attributes of the component.
    // Note that some query methods are inherited from the superclass.
    public void setLabel(String label) {
	this.label = label;
	newLabel();               // Break the label into lines.
	repaint();                // Request a redraw.
	measured = false;         // Note that we need to measure lines.
	invalidate();             // Tell our containers about this
    }
    public void setAlignment(Alignment a) { alignment = a; repaint(); }
    public void setMarginWidth(int mw) { margin_width = mw; repaint(); }
    public void setMarginHeight(int mh) { margin_height = mh; repaint(); }

    // Override this property setter method because we need to remeasure
    public void setFont(Font f) {
	super.setFont(f);         // Tell our superclass about the new font.
	repaint();                // Request a redraw.
	measured = false;         // Note that we need to remeasure lines.
	invalidate();             // Tell our containers about new size
    }

    // Property getter methods.
    public String getLabel() { return label; }
    public Alignment getAlignment() { return alignment; }
    public int getMarginWidth() { return margin_width; }
    public int getMarginHeight() { return margin_height; }
    
    /**
     * This method is called by a layout manager when it wants to
     * know how big we'd like to be.
     */
    public Dimension getPreferredSize() {
	if (!measured) measure();
	return new Dimension(max_width + 2*margin_width,
			     num_lines * line_height + 2*margin_height);
    }

    /**
     * This method is called when the layout manager wants to know
     * the bare minimum amount of space we need to get by.
     */
    public Dimension getMinimumSize() { return getPreferredSize(); }
    
    /**
     * This method draws the component.
     * Note that it handles the margins and the alignment, but that
     * it doesn't have to worry about the color or font--the superclass
     * takes care of setting those in the Graphics object we're passed.
     **/
    public void paintComponent(Graphics g) {
	int x, y;
	Dimension size = this.getSize();
	if (!measured) measure();
	y = line_ascent + (size.height - num_lines * line_height)/2;
	for(int i = 0; i < num_lines; i++, y += line_height) {
	    if (alignment == Alignment.LEFT) x = margin_width;
	    else if (alignment == Alignment.CENTER) 
		x = (size.width - line_widths[i])/2; 
	    else x = size.width - margin_width - line_widths[i];
	    g.drawString(lines[i], x, y);
	}
    }

    /**
     * This internal method breaks a specified label up into an array of lines.
     * It uses the StringTokenizer utility class.
     **/
    protected synchronized void newLabel() {
	StringTokenizer t = new StringTokenizer(label, "\n");
	num_lines = t.countTokens();
	lines = new String[num_lines];
	line_widths = new int[num_lines];
	for(int i = 0; i < num_lines; i++) lines[i] = t.nextToken();
    }
    
    /**
     * This internal method figures out how the font is, and how wide each
     * line of the label is, and how wide the widest line is.
     **/
    protected synchronized void measure() {
	FontMetrics fm = this.getFontMetrics(this.getFont());
	line_height = fm.getHeight();
	line_ascent = fm.getAscent();
	max_width = 0;
	for(int i = 0; i < num_lines; i++) {
	    line_widths[i] = fm.stringWidth(lines[i]);
	    if (line_widths[i] > max_width) max_width = line_widths[i];
	}
	measured = true;
    }
}