edu.ku.brc.af.ui.forms.ResultSetController.java Source code

Java tutorial

Introduction

Here is the source code for edu.ku.brc.af.ui.forms.ResultSetController.java

Source

/* Copyright (C) 2015, University of Kansas Center for Research
 * 
 * Specify Software Project, specify@ku.edu, Biodiversity Institute,
 * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package edu.ku.brc.af.ui.forms;

import static edu.ku.brc.ui.UIHelper.createLabel;
import static edu.ku.brc.ui.UIHelper.setControlSize;
import static edu.ku.brc.ui.UIRegistry.getResourceString;

import java.awt.Color;
import java.awt.Component;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;

import org.apache.commons.lang.StringUtils;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

import edu.ku.brc.af.ui.forms.validation.FormValidator;
import edu.ku.brc.af.ui.forms.validation.UIValidator;
import edu.ku.brc.af.ui.forms.validation.ValidationListener;
import edu.ku.brc.ui.CustomDialog;
import edu.ku.brc.ui.IconManager;
import edu.ku.brc.ui.UIHelper;
import edu.ku.brc.ui.UIRegistry;
import edu.ku.brc.ui.UIHelper.CommandType;

/*
 * Creates a ResultSetController with First, Last, Previous, Next, New and Delete buttons.
 * When the current index is -1 then the controller is disabled. Setting the length to zero will
 * automatically set the current index to -1 and disable the controller.
 * 
 * @code_status Complete
 **
 * @author rods
 *
 */
public class ResultSetController implements ValidationListener {
    protected static Border enabledBorder = BorderFactory.createLineBorder(new Color(64, 64, 64));
    protected static Border disabledBorder = BorderFactory.createLineBorder(Color.GRAY.brighter());

    protected static Color enabledTxtBG = Color.WHITE;
    protected static Color disabledTxtBG = new Color(225, 225, 225);//Color.WHITE.darker();

    protected List<ResultSetControllerListener> listeners = new ArrayList<ResultSetControllerListener>();

    protected FormValidator formValidator = null;

    protected JPanel panel = null;
    protected JButton firstBtn = null;
    protected JButton prevBtn = null;
    protected JLabel recDisp = null;
    protected JButton nextBtn = null;
    protected JButton lastBtn = null;
    protected JButton newRecBtn = null;
    protected JButton delRecBtn = null;
    protected JButton searchRecBtn = null;

    protected int currentInx = 0;
    protected int lastInx = 0;
    protected int numRecords = 0;

    protected boolean isNewObj = false;
    protected boolean doLayoutBtns;

    // Global Key Actions
    private Hashtable<CommandType, JButton> btnsHash = new Hashtable<CommandType, JButton>();

    // Static Members
    private static Hashtable<CommandType, RSAction<CommandType>> commandsHash = null;
    private static ResultSetController currentFocusedRS = null;

    private static ResultSetController backStopRS = null;

    static {
        registerFocusListener();
    }

    /**
     * 
     */
    private ResultSetController() {

    }

    /**
     * Constructor.
     * @param formValidator the form validator that listens for when the index changes to a new object
     * @param addNewBtn indicates it should include the "Add" (New Object) button
     * @param addDelBtn indicates it should include Delete button
     * @param objTitle the title of a single object in the controller (used for building tooltips)
     * @param len the intial length of the List of Objects (can be zero)
     */
    public ResultSetController(final FormValidator formValidator, final boolean addNewBtn, final boolean addDelBtn,
            final boolean addSearchBtn, final String objTitle, final int len, final boolean doLayoutBtns) {
        this.formValidator = formValidator;
        this.doLayoutBtns = doLayoutBtns;

        if (formValidator != null) {
            formValidator.addValidationListener(this);
        }

        String objectTitle = objTitle;
        if (StringUtils.isEmpty(objectTitle)) {
            objectTitle = getResourceString("Item");
            if (StringUtils.isEmpty(objectTitle)) {
                objectTitle = "Item"; // use English if nothing can be found.
            }

        }
        buildRecordNavBar(addNewBtn, addDelBtn, addSearchBtn, objectTitle);

        setLength(len);
    }

    /**
     * 
     */
    public void setupGotoListener() {
        KeyStroke gotoKS = KeyStroke.getKeyStroke(KeyEvent.VK_L,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
        String ACTION_KEY = "GOTO";
        InputMap inputMap = panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        ActionMap actionMap = panel.getActionMap();

        inputMap.put(gotoKS, ACTION_KEY);
        actionMap.put(ACTION_KEY, new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                showGotoRecDlg();
            }
        });
    }

    /**
     * Creates a TooTip using the proper Object
     * @param ttKey the ToolTip key
     * @param objTitle the arg for the tooltip
     * @return the full tooltip
     */
    public static String createTooltip(final String ttKey, final String objTitle) {
        return String.format(getResourceString(ttKey), new Object[] { objTitle });
    }

    /**
     * Creates the UI for the controller.
     * @param addNewBtn indicates it should include the "Add" (New Object) button
     * @param addDelBtn indicates it should include Delete button
     * @param objTitle the title of a single object in the controller (used for building tooltips)
     */
    protected void buildRecordNavBar(final boolean addNewBtn, final boolean addDelBtn, final boolean addSearchBtn,
            final String objTitle) {
        String colDef = "p,2dlu,p,2dlu,max(50dlu;p):grow,2dlu,p,2dlu,p" + (addNewBtn ? ",12px,p" : "")
                + (addDelBtn ? ",2dlu,p" : "") + (addSearchBtn ? ",2dlu,p" : "");

        Insets insets = new Insets(1, 1, 1, 1);
        DefaultFormBuilder rowBuilder = new DefaultFormBuilder(new FormLayout(colDef, "p"));

        firstBtn = UIHelper.createIconBtn("FirstRec", null, null);
        prevBtn = UIHelper.createIconBtn("PrevRec", null, null);
        btnsHash.put(CommandType.Previous, prevBtn);
        btnsHash.put(CommandType.First, firstBtn);

        recDisp = createLabel("  ");
        recDisp.setHorizontalAlignment(SwingConstants.CENTER);
        recDisp.setOpaque(true);
        recDisp.setBackground(Color.WHITE);
        recDisp.setBorder(enabledBorder);
        recDisp.setFont(recDisp.getFont().deriveFont(recDisp.getFont().getSize2D() - 2));

        MouseListener mouseListener = new MouseAdapter() {
            private boolean showIfPopupTrigger(MouseEvent mouseEvent) {
                if (mouseEvent.isPopupTrigger()) {
                    JPopupMenu popupMenu = createPopupMenu();
                    if (popupMenu != null && popupMenu.getComponentCount() > 0) {
                        popupMenu.show(mouseEvent.getComponent(), mouseEvent.getX(), mouseEvent.getY());
                        return true;
                    }
                }
                return false;
            }

            @Override
            public void mousePressed(MouseEvent mouseEvent) {
                showIfPopupTrigger(mouseEvent);
            }

            @Override
            public void mouseReleased(MouseEvent mouseEvent) {
                showIfPopupTrigger(mouseEvent);
            }

            /* (non-Javadoc)
             * @see java.awt.event.MouseAdapter#mouseClicked(java.awt.event.MouseEvent)
             */
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    if (numRecords == 1) {
                        UIRegistry.writeTimedSimpleGlassPaneMsg(getResourceString("OnlyOneRrecordInCon"));
                    } else {
                        showGotoRecDlg();
                    }
                }
            }
        };
        recDisp.addMouseListener(mouseListener);

        nextBtn = UIHelper.createIconBtn("NextRec", null, null);
        lastBtn = UIHelper.createIconBtn("LastRec", null, null);
        btnsHash.put(CommandType.Next, nextBtn);
        btnsHash.put(CommandType.Last, lastBtn);

        firstBtn.setToolTipText(createTooltip("GotoFirstRecordTT", objTitle));
        prevBtn.setToolTipText(createTooltip("GotoPreviousRecordTT", objTitle));
        nextBtn.setToolTipText(createTooltip("GotoNextRecordTT", objTitle));
        lastBtn.setToolTipText(createTooltip("GotoLastRecordTT", objTitle));

        firstBtn.setOpaque(false);
        prevBtn.setOpaque(false);
        nextBtn.setOpaque(false);
        lastBtn.setOpaque(false);

        CellConstraints cc = new CellConstraints();
        rowBuilder.add(firstBtn, cc.xy(1, 1));
        rowBuilder.add(prevBtn, cc.xy(3, 1));
        rowBuilder.add(recDisp, cc.xy(5, 1));
        rowBuilder.add(nextBtn, cc.xy(7, 1));
        rowBuilder.add(lastBtn, cc.xy(9, 1));
        int col = 11;

        if (addNewBtn) {
            if (false) {
                newRecBtn = new JButton("+") {
                    public void setEnabled(boolean enable) {
                        //log.debug("newRecBtn - RS: "+ formValidator.getName() + " " + newRecBtn.hashCode() + " "+enable+"  isNewObj: "+isNewObj);
                        if (formValidator != null && formValidator.getName() != null
                                && formValidator.getName().equals("Collection Object")) {
                            int x = 0;
                            x++;
                            if (enable) {
                                int y = 0;
                                y++;
                            }
                        }
                        if (enable) {
                            int x = 0;
                            x++;
                        }
                        super.setEnabled(enable);
                    }
                };
                setControlSize(newRecBtn);

                ActionListener l = new ActionListener() {
                    public void actionPerformed(ActionEvent ae) {
                        for (ResultSetControllerListener rscl : listeners) {
                            rscl.newRecordAdded();
                        }
                    }
                };
                newRecBtn.addActionListener(l);

            } else {
                newRecBtn = UIHelper.createIconBtn("NewRecord", null, new ActionListener() {
                    public void actionPerformed(ActionEvent ae) {
                        for (ResultSetControllerListener rscl : listeners) {
                            rscl.newRecordAdded();
                        }
                    }
                });
            }

            newRecBtn.setToolTipText(createTooltip("NewRecordTT", objTitle));
            newRecBtn.setEnabled(true);
            newRecBtn.setMargin(insets);
            btnsHash.put(CommandType.NewItem, newRecBtn);

            if (doLayoutBtns) {
                rowBuilder.add(newRecBtn, cc.xy(col, 1));
                col += 2;
            }
        }

        if (addDelBtn) {
            if (false) {
                delRecBtn = new JButton("-") {
                    public void setEnabled(boolean enable) {
                        //log.debug("delRecBtn - RS: "+formValidator.getName() + " " + hashCode() + " "+enable);
                        if (formValidator != null && formValidator.getName() != null
                                && formValidator.getName().equals("Permit")) {
                            int x = 0;
                            x++;
                        }
                        if (!enable) {
                            int x = 0;
                            x++;
                        }
                        super.setEnabled(enable);
                    }
                };
            } else {
                delRecBtn = UIHelper.createIconBtn("DeleteRecord", null, null);
            }
            delRecBtn.setToolTipText(createTooltip("DeleteRecordTT", objTitle));
            delRecBtn.setMargin(insets);
            btnsHash.put(CommandType.DelItem, delRecBtn);

            if (doLayoutBtns) {
                rowBuilder.add(delRecBtn, cc.xy(col, 1));
                col += 2;
            }
        }

        if (addSearchBtn) {
            searchRecBtn = UIHelper.createIconBtn("Search", IconManager.IconSize.Std16, null, null);
            searchRecBtn.setToolTipText(createTooltip("SearchForRecordTT", objTitle));
            searchRecBtn.setMargin(insets);

            if (doLayoutBtns) {
                rowBuilder.add(searchRecBtn, cc.xy(col, 1));
                col += 2;
            }
        }

        firstBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                firstRecord();
            }
        });
        prevBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                prevRecord();
            }
        });
        nextBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                nextRecord();
            }
        });
        lastBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                lastRecord();
            }
        });

        // Make sure it gets centered
        rowBuilder.getPanel().setOpaque(false);
        DefaultFormBuilder outerCenteredPanel = new DefaultFormBuilder(new FormLayout("c:p:g", "p"));
        outerCenteredPanel.add(rowBuilder.getPanel(), cc.xy(1, 1));
        panel = outerCenteredPanel.getPanel();
        panel.setOpaque(false);
    }

    /**
     * 
     */
    protected JPopupMenu createPopupMenu() {
        JPopupMenu popupMenu = new JPopupMenu();
        JMenuItem mi = UIHelper.createLocalizedMenuItem("Go to Record", null, null, true, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                showGotoRecDlg();
            }
        });
        popupMenu.add(mi);
        return popupMenu;
    }

    /**
     * 
     */
    private void firstRecord() {
        if (notifyListenersAboutToChangeIndex(currentInx, 0)) {
            currentInx = 0;
            updateUI();
            notifyListeners();
        }

    }

    /**
     * 
     */
    private void lastRecord() {
        if (notifyListenersAboutToChangeIndex(currentInx, lastInx)) {
            currentInx = lastInx;
            updateUI();
            notifyListeners();
        }

    }

    /**
     * 
     */
    public void prevRecord() {
        if (currentInx < 1)
            return;

        setUIEnabled(false);

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                if (notifyListenersAboutToChangeIndex(currentInx, currentInx - 1)) {
                    // Note: notifyListenersAboutToChangeIndex sometimes can call a method
                    // that ends up setting the currentInx and therefore we should make
                    // sure that by decrementing it will still have a good value
                    if (currentInx > 0) {
                        currentInx--;
                        updateUI();
                        notifyListeners();
                    }
                }
            }
        });
    }

    public void nextRecord() {
        if (currentInx > numRecords - 2)
            return;

        setUIEnabled(false);

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                if (notifyListenersAboutToChangeIndex(currentInx, currentInx + 1)) {
                    currentInx++;
                    updateUI();
                    notifyListeners();
                }
            }
        });
    }

    /**
     * 
     */
    protected void showGotoRecDlg() {
        JTextField tf = UIHelper.createTextField(5);

        CellConstraints cc = new CellConstraints();
        DefaultFormBuilder p = new DefaultFormBuilder(new FormLayout("p,2px,f:p:g", "p"));
        p.add(UIHelper.createI18NLabel("RS_NUM_LBL"), cc.xy(1, 1));
        p.add(tf, cc.xy(3, 1));
        p.setDefaultDialogBorder();

        CustomDialog dlg = new CustomDialog((Frame) null, "", true, CustomDialog.OKCANCEL, p.getPanel());
        dlg.setCustomTitleBar(getResourceString("RS_JUMP_TITLE"));

        UIHelper.centerAndShow(dlg);
        if (!dlg.isCancelled()) {
            String recNumStr = tf.getText();
            if (!recNumStr.isEmpty() && StringUtils.isNumeric(recNumStr)) {
                try {
                    int recNum = Integer.parseInt(recNumStr);
                    if (recNum > 0 && recNum <= lastInx) {
                        setIndex(recNum - 1);
                    }
                } catch (Exception ex) {
                }
            }
        }
    }

    /**
     * Resets the current index to zero.
     * @param enabled true enabled, false not enabled
     */
    public void reset() {
        currentInx = 0;

        updateUI();
    }

    /**
     * Set the the controller to have no items and the number of records to zero.
     * @param enabled true enabled, false not enabled
     */
    public void clear() {
        currentInx = -1;
        numRecords = 0;
        lastInx = -1;
        updateUI();
    }

    /**
     * Sets a new length or size of the controller and adjust the current index to zero or -1.
     * @param len the new length
     */
    public void setLength(final int len) {
        currentInx = len > 0 ? 0 : -1;
        numRecords = len;
        lastInx = numRecords - 1;
        updateUI();
    }

    /**
     * Sets the controller to a new index
     * @param index the new index
     */
    public void setIndex(final int index, final boolean doIndexNotify) {
        if (index < numRecords) {
            int oldInx = currentInx;
            currentInx = index;
            updateUI();
            if (doIndexNotify) {
                notifyListenersAboutToChangeIndex(oldInx, currentInx);
            }
            notifyListeners();
        }
    }

    /**
     * Sets the controller to a new index
     * @param index the new index
     */
    public void setIndex(final int index) {
        setIndex(index, true);
    }

    /**
     * 
     * @return the length (the number of records)
     */
    public int getLength() {
        return numRecords;
    }

    /**
     * @return the current index
     */
    public int getCurrentIndex() {
        return currentInx;
    }

    /**
     * @return the panel
     */
    public JPanel getPanel() {
        return panel;
    }

    /**
     * Enables/Disables the UI according to where we are in the resultset
     */
    public void updateUI() {
        if (panel == null)
            return;

        // This needs to be done first because the enabling of the other buttons
        // may rely on the 'New Btn'
        if (newRecBtn != null) {
            //log.debug("updateUI - RS - formValidator.isTopLevel() "+formValidator.isTopLevel()+" isEnabled() "+formValidator.isEnabled());
            boolean enable = formValidator == null ? false
                    : (formValidator.isTopLevel()
                            || (formValidator.getParent() != null ? formValidator.getParent().isEnabled()
                                    : formValidator.isEnabled()));
            newRecBtn.setEnabled(enable);
        }

        boolean isNewAndValid = newRecBtn == null ? true : newRecBtn.isEnabled();

        /*boolean isNewAndValid = true;
        if (formValidator != null)
        {
        isNewAndValid = !isNewObj || (isNewObj && formValidator.isFormValid());
        }*/
        //System.err.println("isNewObj "+isNewObj+" isNewAndValid "+isNewAndValid+" currentInx "+currentInx);

        firstBtn.setEnabled(currentInx > 0 && isNewAndValid);
        prevBtn.setEnabled(currentInx > 0 && isNewAndValid);
        nextBtn.setEnabled(currentInx < lastInx && isNewAndValid);
        lastBtn.setEnabled(currentInx < lastInx && isNewAndValid);

        boolean enabled = numRecords > 0;

        recDisp.setEnabled(enabled);
        recDisp.setBorder(enabled ? enabledBorder : disabledBorder);
        //recDisp.setBackground(enabled ? enabledTxtBG : disabledTxtBG);
        String lbl = UIRegistry.getLocalizedMessage("RecordControllerDisplay", (currentInx + 1), numRecords);
        recDisp.setText(numRecords > 0 ? lbl : " "); // XXX Move to I18N properties file formatted

        if (delRecBtn != null) {
            delRecBtn.setEnabled(numRecords > 0);
        }

        panel.validate();
    }

    /**
     * @return the recDisp
     */
    public JLabel getRecDisp() {
        return recDisp;
    }

    /**
     * Returns the JButton that is used to create new records.
     * @return the JButton that is used to create new records
     */
    public JButton getNewRecBtn() {
        return newRecBtn;
    }

    /**
     * Returns the JButton that is used to create new records.
     * @return the JButton that is used to create new records
     */
    public JButton getDelRecBtn() {
        return delRecBtn;
    }

    /**
     * Returns the JButton that is used to search for existing records.
     * @return the JButton that is used to search for existing records.
     */
    public JButton getSearchRecBtn() {
        return searchRecBtn;
    }

    /**
     * Adds a listener.
     * @param l the listener
     */
    public void addListener(ResultSetControllerListener l) {
        listeners.add(l);
    }

    /**
     * Remove a listener.
     * @param l the listener
     */
    public void removeListener(ResultSetControllerListener l) {
        listeners.remove(l);
    }

    /**
     * Remove all the listeners.
     */
    public void removeAllListeners() {
        listeners.clear();
    }

    /**
     * Notifies all the listeners that the index has changed.
     */
    protected void notifyListeners() {
        for (ResultSetControllerListener rscl : listeners) {
            rscl.indexChanged(currentInx);
        }
    }

    /**
     * Notifies all the listeners that the index has changed. But any one listener can return
     * false that it isn't OK and then it stops and returns false.
     * @param oldIndex the old index
     * @param newIndex the new index
     * @return returns whether it was ok to change indexes
     */
    protected boolean notifyListenersAboutToChangeIndex(final int oldIndex, final int newIndex) {
        for (ResultSetControllerListener rscl : listeners) {
            boolean isOK = rscl.indexAboutToChange(oldIndex, newIndex);
            if (!isOK) {
                return false;
            }
        }
        return true;
    }

    /**
     * Sets all the UI Enabled/Disabled
     * @param enabled true/false
     */
    public void setUIEnabled(final boolean enabled) {
        if (!enabled) {
            firstBtn.setEnabled(false);
            prevBtn.setEnabled(false);
            nextBtn.setEnabled(false);
            lastBtn.setEnabled(false);
            recDisp.setEnabled(false);

            if (newRecBtn != null) {
                newRecBtn.setEnabled(false);
            }
            if (delRecBtn != null) {
                delRecBtn.setEnabled(false);
            }
        } else {
            updateUI();
        }
    }

    /**
     * @return the isNewObj
     */
    public boolean isNewObj() {
        return isNewObj;
    }

    /**
     * @param isNewObj the isNewObj to set
     */
    public void setNewObj(boolean isNewObj) {
        //System.err.println("this.isNewObj "+this.isNewObj+ " isNewObj "+isNewObj);
        this.isNewObj = isNewObj;
        updateUI();
    }

    /**
     * This is not static because it makes it easier to create the RSAction objects.
     */
    protected void createRSActions() {
        if (commandsHash == null) {
            commandsHash = new Hashtable<CommandType, RSAction<CommandType>>();
            for (CommandType cmdType : CommandType.values()) {
                RSAction<CommandType> action = new RSAction<CommandType>(cmdType);
                commandsHash.put(cmdType, action);
            }
        }
    }

    /**
     * Sets the current (or focused ResultSetController) into the action btns.
     * @param rsc ResultSetController
     */
    protected static void installRS(final ResultSetController rsc) {
        if (commandsHash == null && rsc != null) {
            rsc.createRSActions();
        }

        if (commandsHash != null) {
            for (RSAction<CommandType> rsca : commandsHash.values()) {
                rsca.setRs(rsc);

                if (rsc != null) {
                    JButton btn = rsc.btnsHash.get(rsca.getType());
                    if (btn != null) {
                        KeyStroke ks = UIHelper.getKeyStroke(rsca.getType());
                        String ACTION_KEY = rsca.getType().toString();
                        InputMap inputMap = btn.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
                        ActionMap actionMap = btn.getActionMap();

                        inputMap.put(ks, ACTION_KEY);
                        actionMap.put(ACTION_KEY, rsca);
                        rsca.setBtn(btn);
                    } else {
                        //System.err.println("Btn for ["+rsca.getType()+"] is null");
                    }
                }
            }
        }
    }

    /**
     * 
     */
    private static void registerFocusListener() {
        final KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        focusManager.addPropertyChangeListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent e) {
                Component permanentFocusOwner = null;
                String propName = e.getPropertyName();
                if (propName.equals("permanentFocusOwner")) {
                    permanentFocusOwner = focusManager.getFocusOwner();
                }
                Component comp = permanentFocusOwner;
                while (comp != null && !(comp instanceof MultiView)) {
                    comp = comp.getParent();
                }

                ResultSetController rsc = null;
                boolean fnd = false;
                if (comp instanceof MultiView) {
                    FormViewObj fvo = ((MultiView) comp).getCurrentViewAsFormViewObj();
                    if (fvo != null && fvo.getRsController() != null) {
                        rsc = fvo.getRsController();
                        if (currentFocusedRS == null || currentFocusedRS != rsc) {
                            currentFocusedRS = rsc;
                            fnd = true;
                        }
                    }
                }

                if (!fnd) {
                    currentFocusedRS = backStopRS;
                }

                installRS(currentFocusedRS);

            }
        });
    }

    /**
     * Gets the Mneu Integer from a Key to I18N
     * @param key the Mneu key
     * @return the Integer
     */
    protected static Integer getMneuInt(final String key) {
        String str = getResourceString(key);
        if (StringUtils.isNotEmpty(str)) {
            return new Integer(str.charAt(0));
        }
        return null;
    }

    /**
     * Helper method for creating the menu items.
     * @param menuItem the parent menu it for the menu items
     * @param titleKey the I18N key for the button title
     * @param menuKey the I18N key for the mneumonic 
     * @param cmdType the command
     */
    protected static void createMenuItem(final JMenuItem menuItem, final String titleKey,
            final CommandType cmdType) {
        KeyStroke ks = UIHelper.getKeyStroke(cmdType);
        JMenuItem mi = new JMenuItem(UIRegistry.getInstance().makeAction(commandsHash.get(cmdType),
                getResourceString(titleKey), null, "", ks.getKeyCode(), ks));
        menuItem.add(mi);
    }

    /**
     * Add special MenuItems to the data menu.
     * @param mi the data menu
     */
    public static void addMenuItems(final JMenuItem mi) {
        ResultSetController rsc = new ResultSetController();
        rsc.createRSActions();

        createMenuItem(mi, "DATA_FIRST", CommandType.First);
        createMenuItem(mi, "DATA_PREVIOUS", CommandType.Previous);
        createMenuItem(mi, "DATA_NEXT", CommandType.Next);
        createMenuItem(mi, "DATA_LAST", CommandType.Last);
    }

    /**
     * @param backStopRS the backStopRS to set
     */
    public static void setBackStopRS(ResultSetController backStopRS) {
        ResultSetController.backStopRS = backStopRS;
        installRS(backStopRS);
    }

    //----------------------------------------------------------------------------
    //--
    //----------------------------------------------------------------------------
    class RSAction<T> extends AbstractAction implements PropertyChangeListener {
        protected CommandType type;
        protected ResultSetController rs = null;
        protected JButton btn = null;

        public RSAction(final CommandType type) {
            super();
            this.type = type;
        }

        /* (non-Javadoc)
         * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            if (rs != null && btn != null) {
                // rods (04/26/11) NOTE: the 'doClick' on the button does not work.
                // I think it has to do with the event being dispatched on the UI thread.
                // With this change the call to change the index gets call directly
                //btn.doClick();
                switch (type) {
                case First:
                    rs.firstRecord();
                    break;
                case Previous:
                    rs.prevRecord();
                    break;
                case Next:
                    rs.nextRecord();
                    break;
                case Last:
                    rs.lastRecord();
                    break;
                case Save:
                    break;
                case NewItem:
                    break;
                case DelItem:
                    break;
                }
            }
        }

        public void setEnabled(final boolean enabled) {
            if (!enabled || rs == null) {
                super.setEnabled(false);

            } else if (btn != null) {
                super.setEnabled(btn.isEnabled());
            } else {
                super.setEnabled(false);
            }
        }

        /**
         * @return the btn
         */
        public JButton getBtn() {
            return btn;
        }

        /**
         * @param btn the btn to set
         */
        public void setBtn(JButton btn) {
            if (this.btn != null) {
                this.btn.removePropertyChangeListener(this);
            }

            this.btn = btn;

            if (this.btn != null) {
                super.setEnabled(this.btn.isEnabled());
                this.btn.addPropertyChangeListener(this);
            }
        }

        /**
         * @return the type
         */
        public CommandType getType() {
            return type;
        }

        /**
         * @return the rs
         */
        public ResultSetController getRs() {
            return rs;
        }

        /**
         * @param rs the rs to set
         */
        public void setRs(ResultSetController rs) {
            this.rs = rs;
            setEnabled(rs != null);
        }

        /* (non-Javadoc)
         * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
         */
        public void propertyChange(PropertyChangeEvent evt) {
            if (evt.getPropertyName().equals("enabled")) {
                super.setEnabled((Boolean) evt.getNewValue());
            }
        }

    }

    //-----------------------------------------------------
    // ValidationListener
    //-----------------------------------------------------

    /* (non-Javadoc)
      * @see ValidationListener#wasValidated(UIValidator)
      */
    public void wasValidated(final UIValidator validator) {
        if (formValidator != null && firstBtn.isEnabled() != formValidator.isFormValid()) {
            setUIEnabled(formValidator.isFormValid());
        }

    }

}