Package org.openquark.gems.client.navigator

Source Code of org.openquark.gems.client.navigator.NavEditorPanel$EditorFocusChangeListener

/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Business Objects nor the names of its contributors
*       may be used to endorse or promote products derived from this software
*       without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/


/*
* NavEditorPanel.java
* Creation date: Jul 8, 2003
* By: Frank Worsley
*/
package org.openquark.gems.client.navigator;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.KeyboardFocusManager;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.LayoutFocusTraversalPolicy;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;

import org.openquark.cal.compiler.ModuleTypeInfo;
import org.openquark.cal.compiler.ScopedEntityNamingPolicy;
import org.openquark.cal.compiler.ScopedEntityNamingPolicy.UnqualifiedUnlessAmbiguous;
import org.openquark.cal.metadata.ArgumentMetadata;
import org.openquark.cal.metadata.CALFeatureMetadata;
import org.openquark.cal.metadata.ClassMethodMetadata;
import org.openquark.cal.metadata.FunctionMetadata;
import org.openquark.cal.metadata.FunctionalAgentMetadata;
import org.openquark.cal.metadata.InstanceMethodMetadata;
import org.openquark.gems.client.navigator.NavAddress.NavAddressMethod;


/**
* This class implements the metadata editing component for the CAL navigator.
* It dynamically builds an editing UI depending on what kind of metadata is being
* edited. The UI consists of editing sections embedded within the component.
* Sections can be expanded by the user to reveal additional metadata attributes
* that can be edited.
*
* This component is completely independent of the actual NavFrame class and
* can be used by any class implementing the NavFrameOwner interface.
*
* @author Frank Worsley
*/
class NavEditorPanel extends JPanel implements Scrollable {
   
    private static final long serialVersionUID = 4454976555509376719L;

    /**
     * The focus traversal policy for the editor panel. It ensures that the top
     * editor section will receive initial and default focus.
     * @author Frank Worsley
     */
    private class EditorFocusTraversalPolicy extends LayoutFocusTraversalPolicy {
   
        private static final long serialVersionUID = 3928878956202325352L;

        @Override
        public Component getDefaultComponent(Container c) {
            return editorSections.get(0);
        }
   
        @Override
        public Component getFirstComponent(Container c) {
            return editorSections.get(0);
        }
       
        @Override
        public Component getLastComponent(Container c) {
            return editorSections.get(editorSections.size() - 1);
        }
    }
   
    /**
     * A listener that listens to the component that has focus. If the focused component
     * is an editor section or the child of an editor section it will give the editor
     * section a focused look.
     * @author Frank Worsley
     */
    private class EditorFocusChangeListener implements PropertyChangeListener {
       
        /** The editor section that currently has focus. */
        private NavEditorSection currentFocusedSection = null;
       
        /** The editor section that last had focused (current section if a section currently has focus). */
        private Component lastFocusedSection = null;
               
        /** The component that last had focus. */
        private Component lastFocusedComponent = null;

        public void propertyChange(PropertyChangeEvent evt) {
          
            Component newFocusOwner = (Component) evt.getNewValue();
            Component parent = newFocusOwner;
           
            // Check if the new focus owner is a child of an editor section.
            // If it is then give the section the focused look.           
            while (parent != null) {
               
                if (parent instanceof NavEditorSection) {

                    if (newFocusOwner instanceof NavEditorSection &&
                        newFocusOwner != lastFocusedSection) {

                        // If it's a new section make sure the top title is visible
                        if (lastFocusedSection != newFocusOwner) {
                            JComponent expander = ((NavEditorSection) newFocusOwner).getExpander();
                            expander.scrollRectToVisible(expander.getBounds());
                        }

                        lastFocusedSection = newFocusOwner;
                   
                    } else if (newFocusOwner != lastFocusedComponent) {

                        // We need to convert the coordinates and scroll directly in the
                        // editor pane since some editors may be viewports themselves
                        // and will then try to do the scrolling (ie: JTextField).
                        Rectangle bounds = newFocusOwner.getBounds();
                        Rectangle parentBounds = SwingUtilities.convertRectangle(newFocusOwner.getParent(), bounds, NavEditorPanel.this);
                        scrollRectToVisible(parentBounds);
                        lastFocusedComponent = newFocusOwner;
                    }
                   
                    if (currentFocusedSection != null && parent != currentFocusedSection) {
                        currentFocusedSection.setFocusedLook(false);
                    }
                   
                    if (currentFocusedSection != parent) {
                        currentFocusedSection = (NavEditorSection) parent;
                        currentFocusedSection.setFocusedLook(true);
                    }
                   
                    return;
                }
               
                parent = parent.getParent();
            }

            // clear the previous section's focused look
            if (currentFocusedSection != null) {
                currentFocusedSection.setFocusedLook(false);
                currentFocusedSection = null;
            }
        }
       
        /**
         * Restore the focus to the component of the editor panel that was last focused.
         */
        public void restoreSavedFocus() {
           
            // We try the last component, section and then just do the default component. We have to
            // check if the parent is still this editor panel since editors/sections may have been
            // removed from the panel since the focus was last saved.
           
            if (lastFocusedComponent != null && SwingUtilities.isDescendingFrom(lastFocusedComponent, NavEditorPanel.this)) {
                lastFocusedComponent.requestFocus();
               
            } else if (lastFocusedSection != null && SwingUtilities.isDescendingFrom(lastFocusedSection, NavEditorPanel.this)) {
                lastFocusedSection.requestFocus();
               
            } else {
                getFocusTraversalPolicy().getDefaultComponent(NavEditorPanel.this).requestFocus();
            }
        }
    }   

    /** The navigator owner that owns this editor panel. */
    private final NavFrameOwner owner;

    /** The metadata object being edited by this editor panel. */
    private final CALFeatureMetadata metadata;
   
    /** The address of the object whose metadata is being edited. */
    private final NavAddress address;

    /** The editor sections contained in this editor panel in the order they appear. */
    private final List<NavEditorSection> editorSections = new ArrayList<NavEditorSection>();

    /** The JPanel that contains the editor sections. */
    private final JPanel sectionPanel = new JPanel();
   
    /** The argument editor section, if editing entity metadata. */
    private NavEditorSection argumentSection = null;
   
    /** Whether or not the values stored by the editors in the editor panel have changed. */
    private boolean hasChanged = false;

    /** The label for displaying the name of the metadata being edited. */
    private final JLabel titleLabel = new JLabel();
   
    /** The label for displaying the type of the metadata being edited. */
    private final JLabel typeLabel = new JLabel();
   
    /** The label to display the type of the argument for which editing argument metadata. */
    private final JLabel argTypeLabel = new JLabel();
   
    /** The focus listener that is used to create the focus effects of the editor sections. */
    private EditorFocusChangeListener focusListener;

    /**
     * Constructs a new editor panel to edit the given metadata object.
     * @param owner the owner of this editor panel
     * @param address the address of the metadata to edit
     */
    public NavEditorPanel(NavFrameOwner owner, NavAddress address) {
       
        if (owner == null || address == null) {
            throw new NullPointerException();
        }

        this.owner = owner;
        this.address = address;
        this.metadata = NavAddressHelper.getMetadata(owner, address);
       
        if (metadata == null) {
            throw new IllegalArgumentException("no metadata associated with address: " + address);
        }
       
        initialize();
    }
   
    /**
     * Initializes the UI of the editor panel by creating default components
     * and adding editor sections relevant to the metadata being edited.
     */
    private void initialize() {
       
        // Setup the basic properties
        setBackground(Color.WHITE);
        setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        setLayout(new BorderLayout());

        // Install a focus policy to always give default focus to the topmost editor section
        focusListener = new EditorFocusChangeListener();
        addFocusListener();
        setFocusCycleRoot(true);
        setFocusTraversalPolicy(new EditorFocusTraversalPolicy());
       
        // Add the title labels
        typeLabel.setFont(getFont().deriveFont(Font.BOLD, getFont().getSize() + 2));
        titleLabel.setFont(getFont().deriveFont(Font.BOLD, getFont().getSize() + 12));
        argTypeLabel.setFont(getFont().deriveFont(Font.ITALIC, getFont().getSize() + 12));
        Box vbox = Box.createVerticalBox();
        Box hbox = Box.createHorizontalBox();
        hbox.add(typeLabel);
        hbox.add(Box.createHorizontalGlue());
        vbox.add(hbox);
        hbox = Box.createHorizontalBox();
        hbox.add(titleLabel);
        hbox.add(argTypeLabel);
        hbox.add(Box.createHorizontalGlue());
        vbox.add(hbox);
        add(vbox, BorderLayout.NORTH);
       
        // Add the panel for the editor sections
        sectionPanel.setOpaque(false);
        sectionPanel.setLayout(new BoxLayout(sectionPanel, BoxLayout.Y_AXIS));
        add(sectionPanel, BorderLayout.CENTER);
       
        // Sucks up extra vertical space at the bottom of the section panel
        sectionPanel.add(Box.createVerticalGlue());
       
        // Add the basic metadata editing section
        addSection(new NavFeatureEditorSection(this));

        // Add entity metadata editing sections
        if (metadata instanceof FunctionalAgentMetadata || metadata instanceof InstanceMethodMetadata) {
            addSection(new NavGemEntityEditorSection(this));
           
            boolean hasReturnValue = metadata instanceof FunctionMetadata || metadata instanceof ClassMethodMetadata || metadata instanceof InstanceMethodMetadata;
           
            argumentSection = new NavEntityArgumentEditorSection(this, hasReturnValue);
            addSection(argumentSection);
           
            addSection(new NavExampleEditorSection(this));
        }

        // Add an argument editing section
        if (metadata instanceof ArgumentMetadata) {
            addSection(new NavArgumentEditorSection(this));
        }
       
        // Add a custom attribute section
        addSection(new NavCustomAttributeEditorSection(this));
       
        // Load the metadata
        revert();
       
        // Add the finishing touches
        updateTitleLabel();
        editorSections.get(0).setExpanded(true);
    }

    /**
     * Install a listener for focus events to handle focusing the editing sections.
     */   
    public void addFocusListener() {
        KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        focusManager.addPropertyChangeListener("focusOwner", focusListener);
    }

    /**
     * Removes the focus listener installed by the editor panel. This must be called
     * when the editor panel is disposed off, otherwise it will leak a focus listener.
     */
    private void removeFocusListener() {
        KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        focusManager.removePropertyChangeListener("focusOwner", focusListener);       
    }

    /**
     * Sets focus on the component of the editor panel that should have initial focus.
     */   
    public void setInitialFocus() {
        focusListener.restoreSavedFocus();
    }

    /**
     * Updates the text displayed in the title label to match what is stored
     * in the metadata.
     */   
    private void updateTitleLabel() {

        argTypeLabel.setText(null);
        typeLabel.setText(getTypeString());
        titleLabel.setText(NavAddressHelper.getDisplayText(owner, address, ScopedEntityNamingPolicy.UNQUALIFIED));
       
        if (address.getParameter(NavAddress.ARGUMENT_PARAMETER) != null) {
           
            int argumentNumber = Integer.parseInt(address.getParameter(NavAddress.ARGUMENT_PARAMETER));
            ModuleTypeInfo moduleInfo = owner.getPerspective().getWorkingModuleTypeInfo();
            ScopedEntityNamingPolicy namingPolicy = new UnqualifiedUnlessAmbiguous(moduleInfo);
            NavAddress parentAddress;
            if (address.getMethod() == NavAddress.INSTANCE_METHOD_METHOD) {
                parentAddress = NavAddress.getAddress(address.toFeatureName()); // this strips out the &argument=n parameter
            } else {
                parentAddress = address.withAllStripped();
            }
            String[] typeStrings = NavAddressHelper.getTypeStrings(owner, parentAddress, namingPolicy);
            argTypeLabel.setText(" :: " + typeStrings[argumentNumber]);
        }
    }
   
    /**
     * @return the string to use for the type label
     */
    private String getTypeString() {
       
        NavAddressMethod method = address.getMethod();
       
        if (address.getParameter(NavAddress.ARGUMENT_PARAMETER) != null) {
           
            if (method == NavAddress.INSTANCE_METHOD_METHOD) {
                NavAddress parentAddress = NavAddress.getAddress(address.toFeatureName()); // this strips out the &argument=n parameter
                String parentName = NavAddressHelper.getDisplayText(owner, parentAddress);
               
                return NavigatorMessages.getString("NAV_EditingInstanceMethodArg_Header", parentName);
               
            } else {
                String parentName = NavAddressHelper.getDisplayText(owner, address.withAllStripped());
               
                if (method == NavAddress.FUNCTION_METHOD) {
                    return NavigatorMessages.getString("NAV_EditingFunctionArg_Header", parentName);
                   
                } else if (method == NavAddress.CLASS_METHOD_METHOD) {
                    return NavigatorMessages.getString("NAV_EditingClassMethodArg_Header", parentName);
                   
                } else if (method == NavAddress.DATA_CONSTRUCTOR_METHOD) {
                    return NavigatorMessages.getString("NAV_EditingConstructorArg_Header", parentName);
                   
                } else if (method == NavAddress.COLLECTOR_METHOD) {
                    return NavigatorMessages.getString("NAV_EditingCollectorArg_Header", parentName);
                   
                } else {
                    return NavigatorMessages.getString("NAV_EditingGenericArg_Header");
                }
            }
           
        } else if (method == NavAddress.COLLECTOR_METHOD) {
            return NavigatorMessages.getString("NAV_EditingCollector_Header");
       
        } else if (method == NavAddress.MODULE_METHOD) {
            return NavigatorMessages.getString("NAV_EditingModule_Header");
       
        } else if (method == NavAddress.FUNCTION_METHOD) {
            return NavigatorMessages.getString("NAV_EditingFunction_Header");
       
        } else if (method == NavAddress.TYPE_CLASS_METHOD) {
            return NavigatorMessages.getString("NAV_EditingClass_Header");
       
        } else if (method == NavAddress.TYPE_CONSTRUCTOR_METHOD) {
            return NavigatorMessages.getString("NAV_EditingType_Header");
       
        } else if (method == NavAddress.DATA_CONSTRUCTOR_METHOD) {
            return NavigatorMessages.getString("NAV_EditingConstructor_Header");
       
        } else if (method == NavAddress.CLASS_METHOD_METHOD) {
            return NavigatorMessages.getString("NAV_EditingClassMethod_Header");

        } else if (method == NavAddress.CLASS_INSTANCE_METHOD) {
            return NavigatorMessages.getString("NAV_EditingClassInstance_Header");
           
        } else if (method == NavAddress.INSTANCE_METHOD_METHOD) {
            return NavigatorMessages.getString("NAV_EditingInstanceMethod_Header");

        } else {
            throw new IllegalArgumentException("address type not supported: " + method);
        }
    }
   
    /**
     * Adds a new editor section to this editor panel.
     * @param section the new section to add
     */
    void addSection(NavEditorSection section) {
        editorSections.add(section);
        sectionPanel.add(Box.createVerticalStrut(10));
        sectionPanel.add(section);
    }

    /**
     * Removes all editor sections from this editor panel.
     */   
    void removeAllSections() {
        sectionPanel.removeAll();
        sectionPanel.add(Box.createVerticalGlue());
        editorSections.clear();       
    }
   
    /**
     * Called when the values stored by the editors in the given section have changed.
     * @param section the section whose editors changed
     */
    void sectionChanged(NavEditorSection section) {
        hasChanged = true;
    }
   
    /**
     * @return true if the editor sections in this editor panel have changed since the metadata was last saved
     */
    public boolean hasChanged() {
        return hasChanged;
    }
   
    /**
     * Validates all sections in the editor panel.
     * @return true if all values are valid, false otherwise
     */
    public boolean checkValues() {

        boolean hasErrors = false;

        for (final NavEditorSection section : editorSections) {
            if (!section.doValidate()) {
                hasErrors = true;
            }
        }
       
        return !hasErrors;
    }

    /**
     * Refreshes the metadata being edited. This will display the newly refreshed metadata
     * in the edit boxes, overwriting any changes made by the user.
     */   
    public void refresh() {
        NavAddressHelper.refreshMetadata(owner, address, metadata);
        revert();
    }
   
    /**
     * This will revert the displayed values to the stored values from the metadata object.
     */   
     private void revert() {
       
        for (final NavEditorSection section : editorSections) {
            section.doRevert();
            section.doValidate();
        }

        hasChanged = false;
        updateTitleLabel();
    }

    /**
     * This method tells the editor panel that the editing process should stop.
     * It causes the focus listener to be removed and the editComplete method of the owner
     * of this editor panel to be invoked.
     */   
    void stopEditing() {
        removeFocusListener();
        owner.editComplete(address);
    }

    /**
     * Saves the values stored in the editors of all sections back into the metadata
     * object being edited and saves the metadata back to permanent storage.
     */
    public boolean save() {
       
        // Make sure all sections are valid
        if (!checkValues()) {
            return false;
        }
       
        // Save each section
        for (final NavEditorSection section : editorSections) {
            section.doSave();
        }
       
        // Now try to permanently store the metadata.
        if (saveMetadata()) {
           
            owner.metadataChanged(address);

            hasChanged = false;
            updateTitleLabel();
           
            return true;
        }

        return false;
    }
   
    /**
     * @return a metadata object for the metadata currently being displayed in the editors.
     * The metadata will have been saved into the object that is returned, but it was not
     * saved to permanent storage.
     */
    public CALFeatureMetadata getEditedMetadata() {
       
        // Back up the current metadata.
        CALFeatureMetadata currentMetadata = metadata.copy();
       
        // Save each section
        for (final NavEditorSection section : editorSections) {
            section.doSave();
        }

        // Restore the saved metadata.
        CALFeatureMetadata editedMetadata = metadata.copy();
        currentMetadata.copyTo(metadata);
       
        return editedMetadata;
    }
   
    /**
     * Sets the metadata currently being displayed in the editor fields to the values stored in
     * the given metadata object. The new values will not be saved to permanent storage.
     * @param editedMetadata the new metadata to be edited
     */
    public void setEditedMetadata(CALFeatureMetadata editedMetadata) {
        editedMetadata.copyTo(metadata);
        revert();
        hasChanged = true;
    }
   
    /**
     * Saves the metadata object being edited.
     * @return true if saved successfully, false otherwise
     */
    private boolean saveMetadata() {
        return NavAddressHelper.saveMetadata(owner, address, metadata);
    }
   
    /**
     * @return the navigator owner that is using this editor panel
     */
    NavFrameOwner getNavigatorOwner() {
        return owner;
    }
   
    /**
     * @return the address of the metadata object being edited
     */
    NavAddress getAddress() {
        return address;
    }
   
    /**
     * @return the metadata object being edited by this editor panel
     */
    CALFeatureMetadata getMetadata() {
        return metadata;
    }

    /**
     * @see javax.swing.Scrollable#getScrollableTracksViewportHeight()
     */
    public boolean getScrollableTracksViewportHeight() {
        return false;
    }

    /**
     * @see javax.swing.Scrollable#getScrollableTracksViewportWidth()
     */
    public boolean getScrollableTracksViewportWidth() {
        return true;
    }

    /**
     * @see javax.swing.Scrollable#getPreferredScrollableViewportSize()
     */
    public Dimension getPreferredScrollableViewportSize() {
        return getPreferredSize();
    }

    /**
     * @see javax.swing.Scrollable#getScrollableBlockIncrement(java.awt.Rectangle, int, int)
     */
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
        return 150;
    }

    /**
     * @see javax.swing.Scrollable#getScrollableUnitIncrement(java.awt.Rectangle, int, int)
     */
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
        return 50;
    }
}
TOP

Related Classes of org.openquark.gems.client.navigator.NavEditorPanel$EditorFocusChangeListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.