Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures


Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips


Submit News
Search the site here...

Swing Chapter 20. (Advanced topics) Constructing a Word Processor. Easy for reading, Click here!

Custom Search
Swing Chapter 20. (Advanced topics) Constructing a Word Processor. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 6/9 

Previous Page Previous Page (5/9) - Next Page (7/9) Next Page
Subpages: 1. Word Processor: part I - Introducing RTF
2. Word Processor: part II - Managing fonts
3. Word Processor: part III - Colors and images
4. Word Processor: part IV - Working with styles
5. Word Processor: part V - Clipboard and undo/redo
6. Word Processor: part VI - Advanced font mangement
7. Word Processor: part VII - Paragraph formatting
8. Word Processor: part VIII - Find and replace
9. Word Processor: part IX - Spell checker [using JDBC and SQL]

20.6  Word Processor: part VI - Advanced font management

In section 20.2 we used toolbar components to change manipulate font properties. This is useful for making a quick modification without leaving the main application frame, and is typical for word processor applications. However, all serious editor applications also provide a dialog for the editing of all available font properties from one location. In the following example we'll show how to create such a dialog, which includes components to select various font properties and preview the result.

Figure 20.6 RTF word processor with custom font properties and preview dialog.

<<file figure20-6.gif>>

Figure 20.7 Font dialog wth custom list and list cell renderer for foreground color selection.

<<file figure20-7.gif>>

The Code: WordProcessor.java

see \Chapter20\6

import java.awt.*;

import java.awt.event.*;

import java.io.*;

import java.util.*;

import javax.swing.*;

import javax.swing.text.*;

import javax.swing.event.*;

import javax.swing.border.*;

import javax.swing.text.rtf.*;

import javax.swing.undo.*;

public class WordProcessor extends JFrame


  // Unchanged code from section 20.5

  protected String[] m_fontNames;

  protected String[] m_fontSizes;

  protected FontDialog m_fontDialog;

  protected JMenuBar createMenuBar() {

    // Unchanged code from section 20.5

    GraphicsEnvironment ge = GraphicsEnvironment.


    m_fontNames = ge.getAvailableFontFamilyNames();


    m_cbFonts = new JComboBox(m_fontNames);



    // Unchanged code from section 20.5


    m_fontSizes = new String[] {"8", "9", "10", "11", "12", "14",

      "16", "18", "20", "22", "24", "26", "28", "36", "48", "72"};

    m_cbSizes = new JComboBox(m_fontSizes);



    m_fontDialog = new FontDialog(this, m_fontNames, m_fontSizes);

    // Unchanged code from section 20.5

    JMenu mFormat = new JMenu("Format");


    item = new JMenuItem("Font...");


    lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {


        AttributeSet a = m_doc.getCharacterElement(



        Dimension d1 = m_fontDialog.getSize();

        Dimension d2 = WordProcessor.this.getSize();

        int x = Math.max((d2.width-d1.width)/2, 0);

        int y = Math.max((d2.height-d1.height)/2, 0);

        m_fontDialog.setBounds(x + WordProcessor.this.getX(),

          y + WordProcessor.this.getY(), d1.width, d1.height);


        if (m_fontDialog.getOption()==JOptionPane.OK_OPTION) {









    // Unchanged code from section 20.5

    return menuBar;


  // Unchanged code from section 20.5


// Unchanged code from section 20.5

class FontDialog extends JDialog


  protected int m_option = JOptionPane.CLOSED_OPTION;

  protected OpenList m_lstFontName;

  protected OpenList m_lstFontSize;

  protected MutableAttributeSet m_attributes;

  protected JCheckBox m_chkBold;

  protected JCheckBox m_chkItalic;

  protected JCheckBox m_chkUnderline;

  protected JCheckBox m_chkStrikethrough;

  protected JCheckBox m_chkSubscript;

  protected JCheckBox m_chkSuperscript;

  protected JComboBox m_cbColor;

  protected JLabel m_preview;

  public FontDialog(JFrame parent,

    String[] names, String[] sizes)


    super(parent, "Font", true);

    getContentPane().setLayout(new BoxLayout(getContentPane(),


    JPanel p = new JPanel(new GridLayout(1, 2, 10, 2));

    p.setBorder(new TitledBorder(new EtchedBorder(), "Font"));

    m_lstFontName = new OpenList(names, "Name:");


    m_lstFontSize = new OpenList(sizes, "Size:");



    p = new JPanel(new GridLayout(2, 3, 10, 5));

    p.setBorder(new TitledBorder(new EtchedBorder(), "Effects"));

    m_chkBold = new JCheckBox("Bold");


    m_chkItalic = new JCheckBox("Italic");


    m_chkUnderline = new JCheckBox("Underline");


    m_chkStrikethrough = new JCheckBox("Strikeout");


    m_chkSubscript = new JCheckBox("Subscript");


    m_chkSuperscript = new JCheckBox("Superscript");




    p = new JPanel();

    p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));


    p.add(new JLabel("Color:"));


    m_cbColor = new JComboBox();

    int[] values = new int[] { 0, 128, 192, 255 };

    for (int r=0; r<values.length; r++) {

      for (int g=0; g<values.length; g++) {

        for (int b=0; b<values.length; b++) {

          Color c = new Color(values[r], values[g], values[b]);





    m_cbColor.setRenderer(new ColorComboRenderer());




    p = new JPanel(new BorderLayout());

    p.setBorder(new TitledBorder(new EtchedBorder(), "Preview"));

    m_preview = new JLabel("Preview Font", JLabel.CENTER);




    m_preview.setBorder(new LineBorder(Color.black));

    m_preview.setPreferredSize(new Dimension(120, 40));

    p.add(m_preview, BorderLayout.CENTER);


    p = new JPanel(new FlowLayout());

    JPanel p1 = new JPanel(new GridLayout(1, 2, 10, 2));

    JButton btOK = new JButton("OK");

    ActionListener lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        m_option = JOptionPane.OK_OPTION;






    JButton btCancel = new JButton("Cancel");

    lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        m_option = JOptionPane.CANCEL_OPTION;










    Dimension d1 = getSize();

    Dimension d2 = parent.getSize();

    int x = Math.max((d2.width-d1.width)/2, 0);

    int y = Math.max((d2.height-d1.height)/2, 0);

    setBounds(x, y, d1.width, d1.height);

    ListSelectionListener lsel = new ListSelectionListener() {

      public void valueChanged(ListSelectionEvent e) {






    lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {








  public void setAttributes(AttributeSet a) {

    m_attributes = new SimpleAttributeSet(a);

    String name = StyleConstants.getFontFamily(a);


    int size = StyleConstants.getFontSize(a);












  public AttributeSet getAttributes() {

    if (m_attributes == null)

      return null;



















    return m_attributes;


  public int getOption() { return m_option; }

  protected void updatePreview() {

    String name = m_lstFontName.getSelected();

    int size = m_lstFontSize.getSelectedInt();

    if (size <= 0)


    int style = Font.PLAIN;

    if (m_chkBold.isSelected())

      style |= Font.BOLD;

    if (m_chkItalic.isSelected())

      style |= Font.ITALIC;

    // Bug Alert! This doesn't work if only style is changed.

    Font fn = new Font(name, style, size);


    Color c = (Color)m_cbColor.getSelectedItem();





class OpenList extends JPanel

  implements ListSelectionListener, ActionListener


  protected JLabel m_title;

  protected JTextField m_text;

  protected JList m_list;

  protected JScrollPane m_scroll;

  public OpenList(String[] data, String title) {


    m_title = new JLabel(title, JLabel.LEFT);


    m_text = new JTextField();



    m_list = new JList(data);



    m_scroll = new JScrollPane(m_list);



  public void setSelected(String sel) {

    m_list.setSelectedValue(sel, true);



  public String getSelected() { return m_text.getText(); }

  public void setSelectedInt(int value) {



  public int getSelectedInt() {

    try {

      return Integer.parseInt(getSelected());


    catch (NumberFormatException ex) { return -1; }


  public void valueChanged(ListSelectionEvent e) {

    Object obj = m_list.getSelectedValue();

    if (obj != null)



  public void actionPerformed(ActionEvent e) {

    ListModel model = m_list.getModel();

    String key = m_text.getText().toLowerCase();

    for (int k=0; k<model.getSize(); k++) {

      String data = (String)model.getElementAt(k);

      if (data.toLowerCase().startsWith(key)) {

        m_list.setSelectedValue(data, true);





  public void addListSelectionListener(ListSelectionListener lst) {



  public Dimension getPreferredSize() {

    Insets ins = getInsets();

    Dimension d1 = m_title.getPreferredSize();

    Dimension d2 = m_text.getPreferredSize();

    Dimension d3 = m_scroll.getPreferredSize();

    int w = Math.max(Math.max(d1.width, d2.width), d3.width);

    int h = d1.height + d2.height + d3.height;

    return new Dimension(w+ins.left+ins.right,



  public Dimension getMaximumSize() {

    Insets ins = getInsets();

    Dimension d1 = m_title.getMaximumSize();

    Dimension d2 = m_text.getMaximumSize();

    Dimension d3 = m_scroll.getMaximumSize();

    int w = Math.max(Math.max(d1.width, d2.width), d3.width);

    int h = d1.height + d2.height + d3.height;

    return new Dimension(w+ins.left+ins.right,



  public Dimension getMinimumSize() {

    Insets ins = getInsets();

    Dimension d1 = m_title.getMinimumSize();

    Dimension d2 = m_text.getMinimumSize();

    Dimension d3 = m_scroll.getMinimumSize();

    int w = Math.max(Math.max(d1.width, d2.width), d3.width);

    int h = d1.height + d2.height + d3.height;

    return new Dimension(w+ins.left+ins.right,



  public void doLayout() {

    Insets ins = getInsets();

    Dimension d = getSize();

    int x = ins.left;

    int y = ins.top;

    int w = d.width-ins.left-ins.right;

    int h = d.height-ins.top-ins.bottom;

    Dimension d1 = m_title.getPreferredSize();

    m_title.setBounds(x, y, w, d1.height);

    y += d1.height;

    Dimension d2 = m_text.getPreferredSize();

    m_text.setBounds(x, y, w, d2.height);

    y += d2.height;

    m_scroll.setBounds(x, y, w, h-y);



class ColorComboRenderer extends JPanel implements ListCellRenderer


  protected Color m_color = Color.black;

  protected Color m_focusColor =

    (Color) UIManager.get("List.selectionBackground");

  protected Color m_nonFocusColor = Color.white;

  public Component getListCellRendererComponent(JList list,

   Object obj, int row, boolean sel, boolean hasFocus)


    if (hasFocus || sel)

      setBorder(new CompoundBorder(

        new MatteBorder(2, 10, 2, 10, m_focusColor),

        new LineBorder(Color.black)));


      setBorder(new CompoundBorder(

        new MatteBorder(2, 10, 2, 10, m_nonFocusColor),

        new LineBorder(Color.black)));

    if (obj instanceof Color)

      m_color = (Color) obj;

    return this;


  public void paintComponent(Graphics g) {





Understanding the Code

Class WordProcessor

Three new instance variables are added:

String[] m_fontNames: array of available font family names.

String[] m_fontSizes: array of font sizes.

FontDialog m_fontDialog: custom font properties and preview dialog.

These arrays were used earlier as local variables to create the toolbar combobox components. Since we need to use them in our font dialog as well, we decided to make them public instance variables (this requires minimal changes to the createMenuBar() method).

Note: Reading the list of available fonts takes a significant amount of time. For performance reasons it is best to do this once in a program.

A new menu item titled "Font..." is now added to the "Format" menu. When the corresponding ActionListener is invoked, the application is repainted, the attributes of the character element corresponding to the current caret position are retrieved as an AttributeSet instance and passed to the dialog for selection (using its setAttributes()), and the dialog is centered relative to the parent frame and displayed. If the dialog is closed with the "OK" button (determined by checking a value returned by FontDialog's getOption() method), we retrieve the new font attributes with FontDialog.getAttributes(), and assign these attributes to the selected text with our setAttributeSet() method. Finally, our toolbar components are updated with our showAttributes() method.

Class FontDialog

This class extends JDialog and acts as a font properties editor and previewer for our word processor application. Several instance variables are declared:

int m_option: indicates how the dialog is closed: by pressing the "OK" button, by pressing the "Cancel" button, or by closing the dialog window directly from the title bar. The constants defined in JOptionPane are reused for this variable.

MutableAttributeSet m_attributes: a collection of font attributes used to preserve the user's selection.

OpenList m_lstFontName: custom JList sub-class for selecting the font family name.

OpenList m_lstFontSize: custom JList sub-class for selecting the font size.

JCheckBox m_chkBold: checkbox to select the bold attribute.

JCheckBox m_chkItalic: checkbox to select the italic attribute.

JCheckBox m_chkUnderline: checkbox to select the font underline attribute.

JCheckBox m_chkStrikethrough: checkbox to select the font strikethrough attribute.

JCheckBox m_chkSubscript: checkbox to select the font subscript attribute.

JCheckBox m_chkSuperscript: checkbox to select the font superscript attribute.

JComboBox m_cbColor: combobox to select the font foreground color.

JLabel m_preview: label to preview the selections.

The FontDialog constructor first creates a super-class modal dialog titled "Font." The constructor creates and initializes all GUI components used in this dialog. A y-oriented BoxLayout is used to place component groups from top to bottom.

Two OpenList components (see below) are placed at the top to select an available font family name and font size. These components encapsulate a label, text box and list components which work together. They are comparable to editable comboboxes that always keep their drop-down list open. Below the OpenLists, a group of six checkboxes are placed for selecting bold, italic, underline, strikethrough, subscript, and superscript font attributes. JComboBox m_cbColor is placed below this group, and is used to select the font foreground color. 64 Colors are added, and an instance of our custom ColorComboRenderer class (see below) is used as its list cell renderer. JLabel m_preview is used to preview the selected font before applying it to the editing text, and is placed below the foreground color combo box.

Two buttons labeled "OK" and "Cancel" are placed at the bottom of the dialog. They are placed in a panel managed by a 1x2 GridLayout, which is in turn placed in a panel managed by a FlowLayout. This is to ensure the equal sizing and central placement of the buttons. Both receive ActionListeners which hide the dialog and set the m_option instance variable to JOptionPane.OK_OPTION and JOptionPane.CANCEL_OPTION respectively. An application (WordProcessor in our case) will normally check this value once the modal dialog is dismissed by calling its getOption() method. This tells the application whether or not the changes should be ignored (CANCEL_OPTION) or applied (OK_OPTION).

The dialog window is packed to give it a natural size, and is then centered with respect to the parent frame. The m_lstFontName and m_lstFontSize OpenList components each receive the same ListSelectionListener instance which calls our custom updatePreview() method (see below) whenever the list selection is changed. Similarly, two checkboxes and the foreground color combobox receive an ActionListener which does the same thing. This provides dynamic preview of the selected font attributes as soon as any is changed.

Bug Alert! Underline, strikethrough, subscript, and superscript font properties are not supported by the AWT Font class, so they cannot be shown in the JLabel component. This is why the corresponding checkbox components do not receive an ActionListener. As we will see, these properties also do not work properly in RTF documents. They are included in this dialog for completeness, in the hopes that they will work properly in a future Swing release.

The setAttributes() method takes an AttributeSet instance as parameter. It copies this attribute set into a SimpleAttributeSet stored as our m_attributes instance variable. Appropriate font attributes are extracted using StyleConstants methods, and used to assign values to the dialog's controls. Finally the preview label is updated according to these new settings by calling our updatePreview() method. Note that the setAttributes() method is public and is used for data exchange between this dialog and it's owner (in our case WordProcessor).

The getAttributes() method plays an opposite role with respect to setAttributes(). It retrieves data from dialog's controls, packs them into an AttributeSet instance using StyleConstants methods, and returns this set to the caller

The getOption() method returns a code indicating how the dialog was closed by the user. This value should be checked prior to retrieving data from the dialog to determine whether or not the user canceled (JOptionPane.CANCEL_OPTION) or ok'd (JOptionPane.OK_OPTION) the changes.

The updatePreview() method is called to update the font preview label when a font attribute is changed. It retrieves the selected font attributes (family name, size, bold and italic properties) and creates a new Font instance to render the label. The selected color is retrieved from the m_cbColor combobox and set as the label's foreground.

Class OpenList

This component consists of a title label, a text field, and a list in a scroll pane. The user can either select a value from the list, or enter it in the text box manually. OpenList extends JPanel and maintains the following four instance variables:

JLabel m_title: title label used to identify the purpose of this component.

JTextField m_text: editable text field.

JList m_list: list component.

JScrollPane m_scroll: scroll pane containing the list component.

The OpenList constructir assigns a null layout manager because this container manages its child components on its own. The four components listed above are instantiated and simply added to this container.

The setSelected() method sets the text field text to that of the given String, and selects the corresponding item in the list (which is scrolled to show display the newly selected value). The getSelected() method retrieves and returns the selected item as a String.

Methods setSelectedInt()/getSelectedInt() do the same but with int values. These methods are implemented to simplify working with a list of ints.

The valueChanged() and actionPerformed() methods provide coordination between the list component and the text field. The valueChanged() method is called whenever the list selection changes, and will assign the result of a toString() call on the selected item as the text field's text. The actionPerformed() method will be called when the user presses Enter while the text field has the current focus. This implementation performs a case-insensitive search through the list items in an effort to find an item which begins with the entered text. If such an item is found, it is selected.

The public addListSelectionListener() method adds a ListSelectionListener to our list component (which is protected). In this way, external objects can dynamically receive notifications about changes in that list's selection.

The getPreferredSize(), getMaximumSize(), and getMinimumSize() methods calculate and return a preferred, maximum, and minimum dimension of this container respectively. They assume that the three child components (label, text field, and scroll pane containing the list) will be laid out one under another from top to bottom, receiving an equal width and their preferable heights. The doLayout() method actually lays out the components according to this scheme. Note that the insets (resulting from an assigned border, for instance) must always be taken into account (see chapter 4 for more about custom layout management).

Class ColorComboRenderer

This class implements the ListCellRenderer interface (discussed in chapters 9 and 10) and is used to represent various Colors. Three instance variables are defined:

Color m_color: used for the main background color to represent a Color.

Color m_focusColor: used for the thick border color of a selected item.

Color m_nonFocusColor: used for the thick border color of an unselected item.

The getListCellRendererComponent() method is called prior to the rendering of each list item (in our WordProcessor example this list is contained within our foreground colors combo box).  The Color instance is retrieved and stored in the m_color instance variable. This color is used as the renderer's background, while a white matte border is used to surround unselected cells, and a light blue matte border is used to surround a selected cell. The paintComponent() method simply sets the background to m_color and calls the super-class paintComponent() method.

Running the Code

Open an existing RTF file, select a portion of text, and bring up the font dialog. Verify that the initial values correspond to the font attributes of the character element at the current caret position. Try selecting different font attributes and note that the preview component is updated dynamically. Press the "OK" button to apply the selected attributes to the selected text. Also verify that pressing the "Cancel" button does not apply any changes. Figures 20.6 and 20.7 illustrate.

[ Return to Swing (Book) ]

Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color

[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3

[ More in News Section ]

Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy