AppletMenuBarDemo.java Source code

Java tutorial

Introduction

Here is the source code for AppletMenuBarDemo.java

Source

/*
 * Copyright (c) 2000 David Flanagan. All rights reserved. This code is from the
 * book Java Examples in a Nutshell, 2nd 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. You may distribute it non-commercially as
 * long as you retain this notice. For a commercial use license, or to purchase
 * the book (recommended), visit http://www.davidflanagan.com/javaexamples2.
 */

import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Panel;
import java.awt.PopupMenu;
import java.awt.event.MouseEvent;
import java.util.Vector;

public class AppletMenuBarDemo extends Applet {
    public void init() {
        AppletMenuBar menubar = new AppletMenuBar();
        menubar.setForeground(Color.black);
        menubar.setHighlightColor(Color.red);
        menubar.setFont(new Font("helvetica", Font.BOLD, 12));
        this.setLayout(new BorderLayout());
        this.add(menubar, BorderLayout.NORTH);

        PopupMenu file = new PopupMenu();
        file.add("New...");
        file.add("Open...");
        file.add("Save As...");
        PopupMenu edit = new PopupMenu();
        edit.add("Cut");
        edit.add("Copy");
        edit.add("Paste");

        menubar.addMenu("File", file);
        menubar.addMenu("Edit", edit);
    }
}

/*
 * Copyright (c) 2000 David Flanagan. All rights reserved. This code is from the
 * book Java Examples in a Nutshell, 2nd 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. You may distribute it non-commercially as
 * long as you retain this notice. For a commercial use license, or to purchase
 * the book (recommended), visit http://www.davidflanagan.com/javaexamples2.
 */

class AppletMenuBar extends Panel {
    // Menubar contents
    Vector labels = new Vector();

    Vector menus = new Vector();

    // Properties
    Insets margins = new Insets(3, 10, 3, 10); // top, left, bottom, right

    int spacing = 10; // Space between menu labels

    Color highlightColor; // Rollover color for labels

    // internal stuff
    boolean remeasure = true; // Whether the labels need to be remeasured

    int[] widths; // The width of each label

    int[] startPositions; // Where each label starts

    int ascent, descent; // Font metrics

    Dimension prefsize = new Dimension(); // How big do we want to be?

    int highlightedItem = -1; // Which item is the mouse over?

    /**
     * Create a new component that simulates a menubar by displaying the
     * specified labels. Whenever the user clicks the specified label, popup up
     * the PopupMenu specified in the menus array. Elements of the menus arra
     * may be a static PopupMenu object, or a PopupMenuFactory object for
     * dynamically creating menus. Perhaps we'll also provide some other kind of
     * constructor or factory method that reads popup menus out of a config
     * file.
     */
    public AppletMenuBar() {
        // We'd like these kinds of events to be delivered
        enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }

    /** Add a popup menu to the menubar */
    public void addMenu(String label, PopupMenu menu) {
        insertMenu(label, menu, -1);
    }

    /** Insert a popup menu into the menubar */
    public void insertMenu(String label, PopupMenu menu, int index) {
        if (index < 0)
            index += labels.size() + 1; // Position to put it at
        this.add(menu); // Popup belongs to us
        labels.insertElementAt(label, index); // Remember the label
        menus.insertElementAt(menu, index); // Remember the menu
        remeasure = true; // Remeasure everything
        invalidate(); // Container must relayout
    }

    /** Property accessor methods for margins property */
    public Insets getMargins() {
        return (Insets) margins.clone();
    }

    public void setMargins(Insets margins) {
        this.margins = margins;
        remeasure = true;
        invalidate();
    }

    /** Property accessor methods for spacing property */
    public int getSpacing() {
        return spacing;
    }

    public void setSpacing(int spacing) {
        if (this.spacing != spacing) {
            this.spacing = spacing;
            remeasure = true;
            invalidate();
        }
    }

    /** Accessor methods for highlightColor property */
    public Color getHighlightColor() {
        if (highlightColor == null)
            return getForeground();
        else
            return highlightColor;
    }

    public void setHighlightColor(Color c) {
        if (highlightColor != c) {
            highlightColor = c;
            repaint();
        }
    }

    /** We override the setFont() method so we can remeasure */
    public void setFont(Font f) {
        super.setFont(f);
        remeasure = true;
        invalidate();
    }

    /** Override these color property setter method so we can repaint */
    public void setForeground(Color c) {
        super.setForeground(c);
        repaint();
    }

    public void setBackground(Color c) {
        super.setBackground(c);
        repaint();
    }

    /**
     * This method is called to draw tell the component to redraw itself. If we
     * were implementing a Swing component, we'd override paintComponent()
     * instead
     */
    public void paint(Graphics g) {
        if (remeasure)
            measure(); // Remeasure everything first, if needed

        // Figure out Y coordinate to draw at
        Dimension size = getSize();
        int baseline = size.height - margins.bottom - descent;
        // Set the font to draw with
        g.setFont(getFont());
        // Loop through the labels
        int nummenus = labels.size();
        for (int i = 0; i < nummenus; i++) {
            // Set the drawing color. Highlight the current item
            if ((i == highlightedItem) && (highlightColor != null))
                g.setColor(getHighlightColor());
            else
                g.setColor(getForeground());

            // Draw the menu label at the position computed in measure()
            g.drawString((String) labels.elementAt(i), startPositions[i], baseline);
        }

        // Now draw a groove at the bottom of the menubar.
        Color bg = getBackground();
        g.setColor(bg.darker());
        g.drawLine(0, size.height - 2, size.width, size.height - 2);
        g.setColor(bg.brighter());
        g.drawLine(0, size.height - 1, size.width, size.height - 1);
    }

    /** Called when a mouse event happens over the menubar */
    protected void processMouseEvent(MouseEvent e) {
        int type = e.getID(); // What type of event?
        int item = findItemAt(e.getX()); // Over which menu label?

        if (type == MouseEvent.MOUSE_PRESSED) {
            // If it was a mouse down event, then pop up the menu
            if (item == -1)
                return;
            Dimension size = getSize();
            PopupMenu pm = (PopupMenu) menus.elementAt(item);
            if (pm != null)
                pm.show(this, startPositions[item] - 3, size.height);

        } else if (type == MouseEvent.MOUSE_EXITED) {
            // If the mouse left the menubar, then unhighlight
            if (highlightedItem != -1) {
                highlightedItem = -1;
                if (highlightColor != null)
                    repaint();
            }
        } else if ((type == MouseEvent.MOUSE_MOVED) || (type == MouseEvent.MOUSE_ENTERED)) {
            // If the mouse moved, change the highlighted item, if necessary
            if (item != highlightedItem) {
                highlightedItem = item;
                if (highlightColor != null)
                    repaint();
            }
        }
    }

    /** This method is called when the mouse moves */
    protected void processMouseMotionEvent(MouseEvent e) {
        processMouseEvent(e);
    }

    /** This utility method converts an X coordinate to a menu label index */
    protected int findItemAt(int x) {
        // This could be a more efficient search...
        int nummenus = labels.size();
        int halfspace = spacing / 2 - 1;
        int i;
        for (i = nummenus - 1; i >= 0; i--) {
            if ((x >= startPositions[i] - halfspace) && (x <= startPositions[i] + widths[i] + halfspace))
                break;
        }
        return i;
    }

    /**
     * Measure the menu labels, and figure out their positions, so we can
     * determine when a click happens, and so we can redraw efficiently.
     */
    protected void measure() {
        // Get information about the font
        FontMetrics fm = this.getFontMetrics(getFont());
        // Remember the basic font size
        ascent = fm.getAscent();
        descent = fm.getDescent();
        // Create arrays to hold the measurements and positions
        int nummenus = labels.size();
        widths = new int[nummenus];
        startPositions = new int[nummenus];

        // Measure the label strings and
        // figure out the starting position of each label
        int pos = margins.left;
        for (int i = 0; i < nummenus; i++) {
            startPositions[i] = pos;
            String label = (String) labels.elementAt(i);
            widths[i] = fm.stringWidth(label);
            pos += widths[i] + spacing;
        }

        // Compute our preferred size from this data
        prefsize.width = pos - spacing + margins.right;
        prefsize.height = ascent + descent + margins.top + margins.bottom;

        // We've don't need to be remeasured anymore.
        remeasure = false;
    }

    /**
     * These methods tell the container how big the menubar wants to be.
     *  
     */
    public Dimension getMinimumSize() {
        return getPreferredSize();
    }

    public Dimension getPreferredSize() {
        if (remeasure)
            measure();
        return prefsize;
    }

    /** @deprecated Here for compatibility with Java 1.0 */
    public Dimension minimumSize() {
        return getPreferredSize();
    }

    /** @deprecated Here for compatibility with Java 1.0 */
    public Dimension preferredSize() {
        return getPreferredSize();
    }

    /**
     * This method is called when the underlying AWT component is created. We
     * can't measure ourselves (no font metrics) until this is called.
     */
    public void addNotify() {
        super.addNotify();
        measure();
    }

    /** This method tells the container not to give us keyboard focus */
    public boolean isFocusTraversable() {
        return false;
    }

}