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-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.form;

import java.awt.event.*;
import java.beans.*;
import java.awt.datatransfer.*;
import javax.swing.text.DefaultEditorKit;

import org.openide.*;
import org.openide.actions.PasteAction;
import org.openide.nodes.*;
import org.openide.explorer.*;
import org.openide.awt.UndoRedo;
import org.openide.explorer.view.BeanTreeView;
import org.openide.windows.*;
import org.openide.util.*;
import org.openide.util.actions.SystemAction;
import org.openide.util.actions.ActionPerformer;
import org.openide.util.datatransfer.*;

import org.netbeans.modules.form.actions.TestAction;

/**
 * The ComponentInspector - special explorer for form editor.
 *
 * @author Tomas Pavek
 */

public class ComponentInspector extends TopComponent
                                implements ExplorerManager.Provider
{
    private ExplorerManager explorerManager;

    private TestAction testAction = (TestAction)
                SystemAction.findObject(TestAction.class, true);

    private PasteAction pasteAction = (PasteAction)
                SystemAction.findObject(PasteAction.class, true);

    private CopyCutActionPerformer copyActionPerformer = new CopyCutActionPerformer(true);
    private CopyCutActionPerformer cutActionPerformer = new CopyCutActionPerformer(false);
    private DeleteActionPerformer deleteActionPerformer = new DeleteActionPerformer();

    private ClipboardListener clipboardListener;

    /** Currently focused form or null if no form is opened/focused */
    private FormEditorSupport focusedForm;

    private EmptyInspectorNode emptyInspectorNode;
    
    private BeanTreeView treeView;

    /** Default icon base for control panel. */
    private static final String EMPTY_INSPECTOR_ICON_BASE =
        "org/netbeans/modules/form/resources/emptyInspector"; // NOI18N

    /** The icon for ComponentInspector */
    private static final String iconURL =
        "org/netbeans/modules/form/resources/inspector.png"; // NOI18N

    private static ComponentInspector instance;

    // ------------
    // construction (ComponentInspector is a singleton)

    /** Gets default instance. Don't use directly, it reserved for '.settings' file only,
     * i.e. deserialization routines, otherwise you can get non-deserialized instance. */
    public static synchronized ComponentInspector getDefault() {
        if (instance == null)
            instance = new ComponentInspector();
        return instance;
    }

    /** Finds default instance. Use in client code instead of {@link #getDefault()}. */
    public static synchronized ComponentInspector getInstance() {
        if (instance == null) {
            TopComponent tc = WindowManager.getDefault().findTopComponent("ComponentInspector"); // NOI18N
            if (instance == null) {
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, new IllegalStateException(
                    "Can not find ComponentInspector component for its ID. Returned " + tc)); // NOI18N
                instance = new ComponentInspector();
            }
        }
        return instance;
    }

    /** Overriden to explicitely set persistence type of ComponentInspector
     * to PERSISTENCE_ALWAYS */
    public int getPersistenceType() {
        return TopComponent.PERSISTENCE_ALWAYS;
    }

    private ComponentInspector() {
        explorerManager = new ExplorerManager();

        associateLookup(
            ExplorerUtils.createLookup(explorerManager, setupActionMap(getActionMap()))
        );

        emptyInspectorNode = new EmptyInspectorNode();
        explorerManager.setRootContext(emptyInspectorNode);

        explorerManager.addPropertyChangeListener(new NodeSelectionListener());

        setLayout(new java.awt.BorderLayout());
        createComponents();

        setIcon(Utilities.loadImage(iconURL));
        setName(FormUtils.getBundleString("CTL_InspectorTitle")); // NOI18N
        setToolTipText(FormUtils.getBundleString("HINT_ComponentInspector")); // NOI18N
    }

    javax.swing.ActionMap setupActionMap(javax.swing.ActionMap map) {
        map.put(DefaultEditorKit.copyAction, copyActionPerformer);
        map.put(DefaultEditorKit.cutAction, cutActionPerformer);
        //map.put(DefaultEditorKit.pasteAction, ExplorerUtils.actionPaste(explorerManager));
        map.put("delete", deleteActionPerformer); // NOI18N

        return map;
    }

    private void createComponents() {
        treeView = new BeanTreeView();
        treeView.setDragSource(true);
        treeView.setDropTarget(true);
        treeView.getAccessibleContext().setAccessibleName(
            FormUtils.getBundleString("ACS_ComponentTree")); // NOI18N
        treeView.getAccessibleContext().setAccessibleDescription(
            FormUtils.getBundleString("ACSD_ComponentTree")); // NOI18N
        add(java.awt.BorderLayout.CENTER, treeView);
    }

    // --------------
    // overriding superclasses, implementing interfaces

    // ExplorerManager.Provider
    public ExplorerManager getExplorerManager() {
        return explorerManager;
    }

    public UndoRedo getUndoRedo() {
        UndoRedo ur = focusedForm != null ?
                          focusedForm.getFormUndoRedoManager() : null;
        return ur != null ? ur : super.getUndoRedo();
    }

    public HelpCtx getHelpCtx() {
        return new HelpCtx("gui.component-inspector"); // NOI18N
    }

    /** Replaces this in object stream. */
    public Object writeReplace() {
        return new ResolvableHelper();
    }

    protected void componentActivated() {
        attachActions();
    }

    protected void componentDeactivated() {
        detachActions();
    }

    // ------------
    // activating and focusing

    synchronized void attachActions() {
        ExplorerUtils.activateActions(explorerManager, true);
        updatePasteAction();

        Clipboard c = getClipboard();
        if (c instanceof ExClipboard) {
            ExClipboard clip = (ExClipboard) c;
            if (clipboardListener == null)
                clipboardListener = new ClipboardChangesListener();
            clip.addClipboardListener(clipboardListener);
        }
    }

    synchronized void detachActions() {
        ExplorerUtils.activateActions(explorerManager, false);

        Clipboard c = getClipboard();
        if (c instanceof ExClipboard) {
            ExClipboard clip = (ExClipboard) c;
            clip.removeClipboardListener(clipboardListener);
        }
    }

    /** This method focuses the ComponentInspector on given form.
     * @param form the form to focus on
     */
    public void focusForm(final FormEditorSupport form) {
        if (focusedForm != form)
            focusFormInAwtThread(form, 0);
    }

    /** This method focuses the ComponentInspector on given form.
     * @param form the form to focus on
     * @param visible true to open inspector, false to close
     */
    public void focusForm(final FormEditorSupport form, boolean visible) {
        if (focusedForm != form)
            focusFormInAwtThread(form, visible ? 1 : -1);
    }

    private void focusFormInAwtThread(final FormEditorSupport form,
                                      final int visibility) {
        if (java.awt.EventQueue.isDispatchThread()) {
            focusFormImpl(form, visibility);
        }
        else {
            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    focusFormImpl(form, visibility);
                }
            });
        }
    }

    private void focusFormImpl(FormEditorSupport form, int visibility) {
        focusedForm = form;

        if (form == null) {
            testAction.setFormModel(null);

            // swing memory leak workaround
            removeAll();
            createComponents();

            getExplorerManager().setRootContext(emptyInspectorNode);
        }
        else {
            testAction.setFormModel(form.getFormModel());

            Node formNode = form.getFormRootNode();
            if (formNode == null) { // form not loaded yet, should not happen
                System.err.println("Warning: FormEditorSupport.getFormRootNode() returns null"); // NOI18N
                getExplorerManager().setRootContext(emptyInspectorNode);
            }
            else
                getExplorerManager().setRootContext(formNode);
        }

        if (visibility > 0)
            open();
        else if (visibility < 0)
            close();
    }

    public FormEditorSupport getFocusedForm() {
        return focusedForm;
    }

    /** Called to synchronize with FormDesigner. Invokes NodeSelectionListener.
     */
    void setSelectedNodes(Node[] nodes, FormEditorSupport form)
        throws PropertyVetoException
    {
        if (form == focusedForm)
            getExplorerManager().setSelectedNodes(nodes);
    }

    Node[] getSelectedNodes() {
        return getExplorerManager().getSelectedNodes();
    }

    // ---------------
    // actions
    
    // fix of issue 42082
    private void updatePasteAction() {
        if(java.awt.EventQueue.isDispatchThread()) {
            updatePasteActionInAwtThread();
        } else {
            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    updatePasteActionInAwtThread();
                }
            });
        }
    }

    private void updatePasteActionInAwtThread() {
        Node[] selected = getExplorerManager().getSelectedNodes();
        if (selected != null && selected.length == 1) {
            // exactly one node must be selected
            Clipboard clipboard = getClipboard();
            Transferable trans = clipboard.getContents(this); // [this??]
            if (trans != null) {
                Node node = selected[0];
                PasteType[] pasteTypes = node.getPasteTypes(trans);
                if (pasteTypes.length != 0) {
                    // transfer accepted by the node, we are done
                    pasteAction.setPasteTypes(pasteTypes);
                    return;
                }

                boolean multiFlavor = false;
                try {
                    multiFlavor = trans.isDataFlavorSupported(
                                    ExTransferable.multiFlavor);
                }
                catch (Exception e) {} // ignore, should not happen

                if (multiFlavor) {
                    // The node did not accept whole multitransfer as is - try
                    // to break it into individual transfers and paste them in
                    // sequence instead.
                    try {
                        MultiTransferObject mto = (MultiTransferObject)
                            trans.getTransferData(ExTransferable.multiFlavor);

                        int n = mto.getCount(), i;
                        Transferable[] t = new Transferable[n];
                        PasteType[] p = new PasteType[n];

                        for (i=0; i < n; i++) {
                            t[i] = mto.getTransferableAt(i);
                            pasteTypes = node.getPasteTypes(t[i]);
                            if (pasteTypes.length == 0)
                                break;

                            p[i] = pasteTypes[0]; // ??
                        }

                        if (i == n) { // all individual transfers accepted
                            pasteAction.setPasteTypes(
                                new PasteType[] { new MultiPasteType(t, p) });
                            return;
                        }
                    }
                    catch (Exception ex) {
                        org.openide.ErrorManager.getDefault().notify(org.openide.ErrorManager.INFORMATIONAL, ex);
                    }
                }
            }
        }

        pasteAction.setPasteTypes(null);
    }

    private boolean checkNodeParents(Node node, java.util.Map set) {
        if (set.get(node) != null)
            return false; // the node is in the set (as parent of another node)
        
        // 'this' means the original node (not parent)
        set.put(node, this);
 
        node = node.getParentNode();
        while (node != null) {
            if (set.put(node, node) == this)
                return false; // the parent is also a node already in the set
            node = node.getParentNode();
        }

        return true;
    }

    private Clipboard getClipboard() {
        Clipboard c = (java.awt.datatransfer.Clipboard)
            Lookup.getDefault().lookup(java.awt.datatransfer.Clipboard.class);
        if (c == null)
            c = java.awt.Toolkit.getDefaultToolkit().getSystemClipboard();
        return c;
    }
    
    protected String preferredID() {
        return getClass().getName();
    }
    
    public boolean requestFocusInWindow() {
        super.requestFocusInWindow();
        return treeView.requestFocusInWindow();
    }

    // ---------------
    // innerclasses

    // listener on nodes selection (ExplorerManager)
    private class NodeSelectionListener implements PropertyChangeListener,
                                                   ActionListener, Runnable
    {
        private javax.swing.Timer timer;

        NodeSelectionListener() {
            timer = new javax.swing.Timer(150, this);
            timer.setCoalesce(true);
            timer.setRepeats(false);
        }

        public void propertyChange(PropertyChangeEvent evt) {
            if (!ExplorerManager.PROP_SELECTED_NODES.equals(evt.getPropertyName()))
                return;

            FormDesigner designer;
            if (focusedForm == null
                    || (designer = focusedForm.getFormDesigner()) == null)
                return;

            Node[] selectedNodes = getExplorerManager().getSelectedNodes();

            if (designer.getDesignerMode() == FormDesigner.MODE_CONNECT) {
                // specially handle node selection in connection mode
                if (selectedNodes.length > 0) {
                    RADComponentCookie cookie = (RADComponentCookie)
                        selectedNodes[0].getCookie(RADComponentCookie.class);
                    if (cookie != null
                        && cookie.getRADComponent() == designer.getConnectionSource()
                        && selectedNodes.length > 1)
                    {
                        cookie = (RADComponentCookie)
                            selectedNodes[selectedNodes.length-1]
                                .getCookie(RADComponentCookie.class);
                    }
                    if (cookie != null)
                        designer.connectBean(cookie.getRADComponent(), true);
                }
            }
            else if (TopComponent.getRegistry().getActivated()
                     == ComponentInspector.this)
            {   // the change comes from ComponentInspector => synchronize FormDesigner
                designer.clearSelectionImpl();
                for (int i=0; i < selectedNodes.length; i++) {
                    FormCookie formCookie = (FormCookie)
                        selectedNodes[i].getCookie(FormCookie.class);
                    if (formCookie != null) {
                        Node node = formCookie.getOriginalNode();
                        if (node instanceof RADComponentNode)
                            designer.addComponentToSelectionImpl(
                                ((RADComponentNode)node).getRADComponent());
                    }
                }
                designer.repaintSelection();
            }

            // refresh nodes' lookup with current set of cookies
            for (int i=0; i < selectedNodes.length; i++)
                ((FormNode)selectedNodes[i]).updateCookies();

            // restart waiting for expensive part of the update
            timer.restart();
        }

        public void actionPerformed(ActionEvent evt) { // invoked by Timer
            java.awt.EventQueue.invokeLater(this); // replan to EventQueue thread
        }

        /** Updates activated nodes and actions. It is executed via timer 
         * restarted each time a new selection change appears - if they come
         * quickly e.g. due to the user is holding a cursor key, this
         * (relatively time expensive update) is done only at the end.
         */
        public void run() {
            Node[] selectedNodes = getExplorerManager().getSelectedNodes();
            setActivatedNodes(selectedNodes);
            // set activated nodes also on FormDesigner - to keep it in sync
            FormDesigner designer = focusedForm != null ?
                                    focusedForm.getFormDesigner() : null;
            if (designer != null)
                designer.setActivatedNodes(selectedNodes);

            updatePasteAction();

            timer.stop();
        }
    }

    // listener on clipboard changes
    private class ClipboardChangesListener implements ClipboardListener {
        public void clipboardChanged(ClipboardEvent ev) {
            if (!ev.isConsumed())
                updatePasteAction();
        }
    }

    // performer for DeleteAction
    private class DeleteActionPerformer extends javax.swing.AbstractAction
                                        implements ActionPerformer, Mutex.Action
    {
        private Node[] nodesToDestroy;

        public void actionPerformed(ActionEvent e) {
            performAction(null);
        }

        public void performAction(SystemAction action) {
            Node[] selected = getExplorerManager().getSelectedNodes();

            if (selected == null || selected.length == 0)
                return;

            for (int i=0; i < selected.length; i++)
                if (!selected[i].canDestroy())
                    return;

            if (!confirmDelete(selected)) // ExplorerPanel.isConfirmDelete() ??
                return;

            try { // clear nodes selection first
                getExplorerManager().setSelectedNodes(new Node[0]);
            }
            catch (PropertyVetoException e) {} // cannot be vetoed

            nodesToDestroy = selected;
            if (java.awt.EventQueue.isDispatchThread())
                doDelete();
            else // reinvoke synchronously in AWT thread
                Mutex.EVENT.readAccess(this);
        }

        public Object run() {
            doDelete();
            return null;
        }

        private void doDelete() {
            if (nodesToDestroy != null) {
                for (int i=0; i < nodesToDestroy.length; i++) {
                    try {
                        nodesToDestroy[i].destroy();
                    }
                    catch (java.io.IOException ex) { // should not happen
                        ex.printStackTrace();
                    }
                }
                nodesToDestroy = null;
            }
        }

        private boolean confirmDelete(Node[] selected) {
            String message;
            String title;
            if (selected.length == 1) {
                message = NbBundle.getMessage(ExplorerUtils.class,
                                              "MSG_ConfirmDeleteObject", // NOI18N
                                              selected[0].getDisplayName());
                title = NbBundle.getMessage(ExplorerUtils.class,
                                            "MSG_ConfirmDeleteObjectTitle"); // NOI18N
            }
            else {
                message = NbBundle.getMessage(ExplorerUtils.class,
                                              "MSG_ConfirmDeleteObjects", // NOI18N
                                               new Integer(selected.length));
                title = NbBundle.getMessage(ExplorerUtils.class,
                                            "MSG_ConfirmDeleteObjectsTitle"); // NOI18N
            }

            NotifyDescriptor desc = new NotifyDescriptor.Confirmation(
                        message, title, NotifyDescriptor.YES_NO_OPTION);

            return NotifyDescriptor.YES_OPTION.equals (
                   DialogDisplayer.getDefault().notify(desc));
        }
    }

    // performer for CopyAction and CutAction
    private class CopyCutActionPerformer extends javax.swing.AbstractAction
                                         implements ActionPerformer
    {
        private boolean copy;

        public CopyCutActionPerformer(boolean copy) {
            this.copy = copy;
        }

        public void actionPerformed(ActionEvent e) {
            performAction(null);
        }

        public void performAction(SystemAction action) {
            Transferable trans;
            Node[] selected = getExplorerManager().getSelectedNodes();

            if (selected == null || selected.length == 0)
                trans = null;
            else if (selected.length == 1)
                trans = getTransferableOwner(selected[0]);
            else {
                Transferable[] transArray = new Transferable[selected.length];
                for (int i=0; i < selected.length; i++)
                    if ((transArray[i] = getTransferableOwner(selected[i]))
                                                                     == null)
                        return;

                trans = new ExTransferable.Multi(transArray);
            }

            if (trans != null) {
                Clipboard clipboard = getClipboard();
                clipboard.setContents(trans, new StringSelection("")); // NOI18N
            }
        }

        private Transferable getTransferableOwner(Node node) {
            try {
                return copy ? node.clipboardCopy() : node.clipboardCut();
            }
            catch (java.io.IOException e) {
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
                return null;
            }
        }
    }

    // paste type used for ExTransferable.Multi
    private class MultiPasteType extends PasteType
                                 implements Mutex.ExceptionAction
    {
        private Transferable[] transIn;
        private PasteType[] pasteTypes;

        MultiPasteType(Transferable[] t, PasteType[] p) {
            transIn = t;
            pasteTypes = p;
        }

        // performs the paste action
        public Transferable paste() throws java.io.IOException {
            if (java.awt.EventQueue.isDispatchThread())
                return doPaste();
            else { // reinvoke synchronously in AWT thread
                try {
                    return (Transferable) Mutex.EVENT.readAccess(this);
                }
                catch (MutexException ex) {
                    Exception e = ex.getException();
                    if (e instanceof java.io.IOException)
                        throw (java.io.IOException) e;
                    else { // should not happen, ignore
                        e.printStackTrace();
                        return ExTransferable.EMPTY;
                    }
                }
            }
        }

        public Object run() throws Exception {
            return doPaste();
        }

        private Transferable doPaste() throws java.io.IOException {
            Transferable[] transOut = new Transferable[transIn.length];
            for (int i=0; i < pasteTypes.length; i++) {
                Transferable newTrans = pasteTypes[i].paste();
                transOut[i] = newTrans != null ? newTrans : transIn[i];
            }
            return new ExTransferable.Multi(transOut);
        }
    }

    // -----------

    // node for empty ComponentInspector
    private static class EmptyInspectorNode extends AbstractNode {
        public EmptyInspectorNode() {
            super(Children.LEAF);
            setIconBase(EMPTY_INSPECTOR_ICON_BASE);
        }
        public boolean canRename() {
            return false;
        }
    }

    final public static class ResolvableHelper implements java.io.Serializable {
        static final long serialVersionUID = 7424646018839457544L;
        public Object readResolve() {
            return ComponentInspector.getDefault();
        }
    }
}
... 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.