alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.explorer.view;

import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import java.util.*;
import java.net.URL;

import javax.swing.*;
import javax.swing.event.*;

import org.openide.explorer.*;
import org.openide.nodes.*;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;
import org.openide.util.HelpCtx;
import org.openide.util.Utilities;

/** An explorer view that shows the context hierarchy in
* a popup menu. Initially, it shows a left button which opens a popup
* menu from the root context and a right button which opens a popup menu from the currently
* explored context.
*
* @author  Ian Formanek, Jaroslav Tulach
*/
public class MenuView extends JPanel {
    /** generated Serialized Version UID */
    static final long serialVersionUID = -4970665063421766904L;

    /** The explorerManager that manages this view */
    transient private ExplorerManager explorerManager;

    /** button to open root view */
    private JButton root;
    /** button to open view from current node */
    private JButton current;
    /** property change listener */
    transient private Listener listener;

    /* This is the constructor implementation
    * recommended by ExplorerView class that only calls the inherited
    * constructor and leaves the initialization for method initialize().
    * @see #initialize  */
    /** Construct a new menu view.
    */
    public MenuView () {
        setLayout (new java.awt.FlowLayout());

        root = new JButton(NbBundle.getBundle (MenuView.class).getString("MenuViewStartFromRoot"));
        add (root);

        current = new JButton(NbBundle.getBundle (MenuView.class).getString("MenuViewStartFromCurrent"));
        add (current);

        init ();
    }

    /** Initializes listeners */
    private void init () {
        root.addMouseListener (listener = new Listener (true));
        current.addMouseListener (new Listener (false));
    }

    private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject ();
        init ();
    }

    /* Initializes view.
    */
    public void addNotify() {
        super.addNotify ();
        explorerManager = ExplorerManager.find (this);
        explorerManager.addPropertyChangeListener (listener);
        doChecks ();
    }

    /* Deinitializes view.
    */
    public void removeNotify() {
        super.removeNotify ();
        explorerManager.removePropertyChangeListener (listener);
        explorerManager = null;
    }

    /** Does some checks */
    private void doChecks () {
        current.setEnabled (explorerManager.getSelectedNodes ().length == 1);
    }

    /** Listener that opens the menu and listens to its actions
    */
    private class Listener extends MouseAdapter
        implements NodeAcceptor, PropertyChangeListener {
        /** from root */
        private boolean root;

        public Listener (boolean root) {
            this.root = root;
        }

        public void mousePressed (MouseEvent e) {

            if (e.getComponent ().isEnabled ()) {
                // open the popup menu
                Node context = null;
                if (!root) {
                    Node[] sel = explorerManager.getSelectedNodes ();
                    if (sel.length > 0) {
                        context = sel[0];
                    }
                }
                if (context == null) {
                    context = explorerManager.getRootContext();
                }

                Menu menu = new Menu (context, listener);

                JPopupMenu popupMenu = menu.getPopupMenu ();
                java.awt.Point p = new java.awt.Point (e.getX (), e.getY ());
                p.x = e.getX() - p.x;
                p.y = e.getY() - p.y;
                SwingUtilities.convertPointToScreen (p, e.getComponent ());
                Dimension popupSize = popupMenu.getPreferredSize ();
                Rectangle screenBounds = Utilities.getUsableScreenBounds(getGraphicsConfiguration());
                if (p.x + popupSize.width > screenBounds.x + screenBounds.width)
                    p.x = screenBounds.x + screenBounds.width - popupSize.width;
                if (p.y + popupSize.height > screenBounds.y + screenBounds.height)
                    p.y = screenBounds.y + screenBounds.height - popupSize.height;
                SwingUtilities.convertPointFromScreen (p, e.getComponent ());
                popupMenu.show(e.getComponent (), p.x, p.y);
            }
        }

        /** Is the set of nodes acceptable?
        * @param nodes the nodes to consider
        * @return true if so
        */
        public boolean acceptNodes(Node[] nodes) {
            // don't allow multiple selections
            if ((nodes == null) || (nodes.length != 1)) {
                return false;
            }
            Node n = nodes[0];
            Node parent = n.getParentNode ();
            if (parent != null) {
                explorerManager.setExploredContext (parent, new Node[] { n });
            }
            return true;
        }

        public void propertyChange (PropertyChangeEvent ev) {
            if (ExplorerManager.PROP_SELECTED_NODES.equals (ev.getPropertyName ())) {
                doChecks ();
            }
        }
        
    }


    /** Menu item representing a node (with children) in a menu hierarchy.
     * One can attach an acceptor to the menu that will be informed
     * each time a user selects an item whether
     * to close the menu or not.
     * The submenu content is taken as a blocking snapshot of the Node's
     * Children at the time of the submenu popup and does not reflect
     * subsequent changes to Node's Children until next popup.
     * This means that the Node's Children should properly implement
     * blocking getNodes(true) in order to show proper content in MenuView.
     */
    public static class Menu extends org.openide.awt.JMenuPlus {
        static final long serialVersionUID =-1505289666675423991L;

        /** The node represented. */
        protected Node node;

        /** Action listener to attach to all menu items. */
        protected NodeAcceptor action;
        
        /** A boolean flag that notes the content was already created */
        private boolean filled = false;

        /** Constructor that assigns the node a default action.
         * For example, open the Explorer or a property sheet.
         * @param node node to represent
         */
        public Menu (Node node) {
            this (node, DEFAULT_LISTENER);
        }
        
        /** Constructor that permits specification of the action on the node.
         *
         * @param node node to represent
         * @param action action called when node is selected
         */
        public Menu (Node node, NodeAcceptor action) {
            this (node, action, true);
        }

        /** @deprecated use {@link MenuView.Menu#MenuView.Menu(Node, NodeAcceptor)}
         */
        public Menu (Node node, Acceptor action) {
            this (node, new AcceptorProxy(action), true);
        }
        
        /** @deprecated use {@link MenuView.Menu#MenuView.Menu(Node, NodeAcceptor, boolean)}
         */
        public Menu (Node node, Acceptor action, boolean setName) {
            this (node, new AcceptorProxy(action), setName);
        }
        
        /** Constructor that permits specification of the action on the node,
         * and permits overriding the name and icon of the menu.
         *
         * @param node node to represent
         * @param action action called when node selected
         * @param setName true to automatically set the name and icon of the item
         */
        public Menu (final Node node, NodeAcceptor action, boolean setName) {
            this.node = node;
            this.action = action;

            if (setName) {
                MenuItem.initialize (this, node);
                HelpCtx help = node.getHelpCtx ();
                if (help != null && ! help.equals (HelpCtx.DEFAULT_HELP) && help.getHelpID () != null)
                    HelpCtx.setHelpIDString (this, help.getHelpID ());
            }
        }

        /** Overriden to fill the submenu with the real content lazily */
        public JPopupMenu getPopupMenu() {
            final JPopupMenu popup = super.getPopupMenu();
            fillSubmenu(popup);
            return popup;
        }

        /** Little class that will reset our status on menu hide */
        private class Helper implements PopupMenuListener {
            private JPopupMenu popup;
            
            Helper(JPopupMenu master) {
                popup = master;
                popup.addPopupMenuListener(this);
            }

            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
                filled = false; // clear the status and stop listening
                popup.removePopupMenuListener(this);
            }
            public void popupMenuCanceled(PopupMenuEvent e) {}
            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {} 
        }
      
        private void fillSubmenu(JPopupMenu popup) {
            if (!filled) {
                filled = true;
                Helper h = new Helper(popup);

                Node[] nodes = node.getChildren().getNodes(true);

                // Fill in the popup.
                removeAll();
                for(int i=0; iMenu for other nodes.
         *
         * @param n node to create element for
         * @return the created node
         */
        protected JMenuItem createMenuItem (Node n) {
            return n.isLeaf () ?
                   (JMenuItem) new MenuItem (n, action) :
                   (JMenuItem) new Menu (n, action);
        }
        
    } // End of class Menu.

    /** Acceptor that can be passed to constructor of {@link MenuView.Menu}.
     * It permits determination of which nodes should be accepted upon a click.
     *
     * @deprecated This interface is almost the same as {@link NodeAcceptor} 
     * so it is redundant and obsoleted. Use {@link NodeAcceptor} 
     * interface instead.
     */
    public static interface Acceptor {
        /** Test whether to accept the node or not. Can also perform some actions (such as opening the node, etc.).
         * @param n the node
         * @return true if the menu should close
         * @deprecated whole interface is obsoleted, use {@link NodeAcceptor#acceptNodes} instead.
         */
         public boolean accept (Node n);
    }

    /** default listener that opens explorer */
    static final NodeAcceptor DEFAULT_LISTENER = new NodeAcceptor () {
                public boolean acceptNodes (Node[] nodes) {
                    // don't allow multiple selections
                    if ((nodes == null) || (nodes.length != 1)) {
                        return false;
                    }
                    Node n = nodes[0];
                    Action a = n.getPreferredAction();
                    if (a != null && a.isEnabled()) {
                        a.actionPerformed(new ActionEvent(n, 0, "")); // NOI18N
                        return true;
                    }
                    return false;
                }
            };

    /** Proxy that allows to use now deprecated MenuView.Acceptor interface
     * on places where NodeAcceptor is requested.
     * This class can be deleted together with MenuView.Acceptor deletion.
     */       
    private static final class AcceptorProxy implements NodeAcceptor {
        private Acceptor original;
        
        AcceptorProxy (Acceptor original) {
            this.original = original;
        }
         
        public boolean acceptNodes (Node[] nodes) {
            // don't allow multiple selections
            if ((nodes == null) || (nodes.length != 1)) {
                return false;
            }
            return original.accept(nodes[0]);
        }
        
    } // end of AcceptorProxy inner class

    /** Menu item that can represent one node in the tree. */
    public static class MenuItem extends JMenuItem implements HelpCtx.Provider {
        /** generated Serialized Version UID */
        static final long serialVersionUID = -918973978614344429L;

        /** The node represented. */
        protected Node node;

        /** The action listener to attach to all menu items. */
        protected NodeAcceptor action;

        /** Construct item for given node with the node's default action.
         * @param node the node to represent
         */
        public MenuItem (Node node) {
            this (node, DEFAULT_LISTENER);
        }

        /** Construct item for given node, specifying an action.
         * @param node the node to represent
         * @param l the acceptor to decide whether to accept this node or not
         */
        public MenuItem (Node node, NodeAcceptor l) {
            this (node, l, true);
        }

        /** @deprecated Use proper constructor with (@link NodeAcceptor). */
        public MenuItem (Node node, Acceptor action) {
            this (node, new AcceptorProxy(action), true);
        }
        
        /** @deprecated Use proper constructor with (@link NodeAcceptor). */
        public MenuItem (Node node, Acceptor action, boolean setName) {
            this (node, new AcceptorProxy(action), setName);
        }
        
        /** Construct item for given node, specifying the action and whether to create the icon and name automatically.
         * @param node the node to represent
         * @param l the acceptor to decide whether to accept this node or not
         * @param setName false if the name and icon should not be set
         */
        public MenuItem (Node node, NodeAcceptor l, boolean setName) {
            super ();

            this.node = node;
            this.action = l;

            if (setName) {
                initialize (this, node);
            }

            // [pnejedly] HelpCtx is now provided through HelpCtx.Provider
            // HelpCtx help = node.getHelpCtx ();
            // if (help != null && ! help.equals (HelpCtx.DEFAULT_HELP) && help.getHelpID () != null)
            //    HelpCtx.setHelpIDString (this, help.getHelpID ());
        }

        /**
         * @return HelpCtx of the underlying node.
         * @since 3.38
         */
        public HelpCtx getHelpCtx() {
            return node.getHelpCtx ();
        }

        /** Inform the acceptor.
         * @param time see superclass
         */
        public void doClick (int time) {
            action.acceptNodes (new Node[] { node });
        }

        /** Initialize an item for a node. */
        static void initialize (final JMenuItem item, final Node node) {
            final class NI implements Runnable, NodeListener, ItemListener {
                public void run () {
                    item.setIcon (new ImageIcon(node.getIcon(java.beans.BeanInfo.ICON_COLOR_16x16)));
                    item.setText (node.getDisplayName ());
                    /*
                          item.setMargin(new java.awt.Insets(0, 0, 0, 0));
                          item.setHorizontalTextPosition(RIGHT);
                          item.setHorizontalAlignment(LEFT);
                    */
                }
                
                public void childrenAdded(NodeMemberEvent ev) {
                }
                public void childrenRemoved(NodeMemberEvent ev) {
                }
                public void childrenReordered(NodeReorderEvent ev) {
                }
                public void nodeDestroyed (NodeEvent ev) {
                }

                /** Update a visualizer (change of name, icon, description, etc.)
                */
                public void propertyChange (PropertyChangeEvent ev) {
                    if (Node.PROP_ICON.equals (ev.getPropertyName())) {
                        Mutex.EVENT.readAccess (this);
                        return;
                    }
                    
                    if (Node.PROP_DISPLAY_NAME.equals (ev.getPropertyName ())) {
                        Mutex.EVENT.readAccess (this);
                        return;
                    }
                }

                
                public void itemStateChanged (ItemEvent ev) {
                }
            }
            
            NI ni = new NI ();
            // update this immediatelly
            ni.run ();
            
            // attach the listener to the menu item, to prevent it from garbage
            // collection until the menu item exists
            item.addItemListener(ni);
            
            // listen to changes in node, but weakly, to allow garbage collection
            // event the node exists
            node.addNodeListener (NodeOp.weakNodeListener (ni, node));
        }
    }


}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.