Package DisplayProject.controls

Source Code of DisplayProject.controls.FillInField$FillinFieldUI$FillInFieldPopup

/*
Copyright (c) 2003-2009 ITerative Consulting Pty Ltd. All Rights Reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:

o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
 
o 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.
   
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder

 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "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.


*/
package DisplayProject.controls;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Hashtable;

import javax.swing.BorderFactory;
import javax.swing.ComboBoxEditor;
import javax.swing.ComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;

import DisplayProject.ArrayFieldModel;
import DisplayProject.CharacterField;
import DisplayProject.Constants;
import DisplayProject.DataField;
import DisplayProject.FixedLengthDocument;
import DisplayProject.FocusHelper;
import DisplayProject.UIutils;
import DisplayProject.actions.WidthPolicy;
import DisplayProject.events.ChildEventHelper;
import DisplayProject.events.ClickListener;
import DisplayProject.events.ClientEventManager;
import DisplayProject.factory.DropFillinFactory;
import DisplayProject.plaf.Win32LookAndFeel;
import DisplayProject.table.ArrayFieldCellHelper;
import Framework.CloneHelper;
import Framework.ErrorMgr;
import Framework.EventHandle;
import Framework.EventManager;
import Framework.ForteKeyboardFocusManager;
import Framework.ParameterHolder;
import Framework.TextData;

import com.sun.java.swing.plaf.windows.WindowsComboBoxUI;
/**
*
* The FillInField class defines drop lists that have some of the capabilities of text fields. A fillin field provides both a fixed element list for the user to select from and a text field in which the user can make a text entry instead of choosing from the list.
*
*/
@SuppressWarnings("serial")
public class FillInField extends AutoResizingComboBox implements CharacterField {
    private ComboBoxEditor editor = null;
    /**
     * The maximum number of characters accepted by the fillin field. If this is 0 the length is unlimited. Note that
     * for auto completing editors this limits only the length of the user input, not the maximum length of string
     * that can be selected, if entries in the list are longer than this value.
     */
    private int maxCharacters = 0;
    private boolean autoComplete = false;
    private boolean listEntriesOnly = true;
    /**
     * CraigM:07/07/2008 - Set this to true if you set listEntriesOnly to true, however, you also want blank entries to be allowed
     */
    private boolean allowBlankEntries = false;
    private boolean showDropdowns = true;
    private boolean validateOnKeystroke = false;
    private int visibleColumns = 0;
    private static final String uiClassID = "FillInField.FillInFieldUI";
    private String oldText = ""; // CraigM:22/01/2009 - The previous text in the FillInField.

    public String getUIClassID() {
        return uiClassID;
    }
    //PM:11/8/07
    public void updateUI() {
        setUI(FillinFieldUI.createUI(this));
        invalidate();
    }

//    private class InputHandle
    public FillInField() {
        super();
        setEditable(true);
        this.editor = new BorderedComboBoxEditor();
        this.setEditor(this.editor);
        this.setRenderer(new BorderedComboBoxRenderer());
        JTextComponent comp = (JTextComponent)this.editor.getEditorComponent();
        comp.setBackground(this.getBackground());
        comp.setDocument(new FixedLengthDocument(0));

        comp.addKeyListener(new ComboBoxKeyHandler());
    }

  /**
     * Determine whether autoComplete is enabled or not. If autoComplete is true, the fill-in field will attempt
     * to populate the editor with a value from the list of possible values.
     * @return
     */
    public boolean isAutoComplete() {
        return autoComplete;
    }

    /**
     * Set whether the combobox autocompletes or not.
     * @param autoComplete
     */
    public void setAutoComplete(boolean autoComplete) {
        boolean oldValue = this.autoComplete;
        this.autoComplete = autoComplete;
        this.firePropertyChange("autoComplete", oldValue, autoComplete);
        if (oldValue != autoComplete) {
            JTextComponent editComponent = (JTextComponent)this.editor.getEditorComponent();
            if (editComponent.getDocument() instanceof FillinManagementDocument) {
                ((FillinManagementDocument)editComponent.getDocument()).uninstall();
            }
            if (autoComplete) {
                editComponent.setDocument(new FillinManagementDocument(this, showDropdowns, listEntriesOnly, maxCharacters));
            }
            else {
                editComponent.setDocument(new FixedLengthDocument(maxCharacters));
            }
        }
    }

    /**
     * The listEntriesOnly property determines if the user can enter a value in the combobox that
     * is not in the list of alternatives presented when the drop-down is active.
     * @return
     */
    public boolean getListEntriesOnly() {
        return listEntriesOnly;
    }

    public void setListEntriesOnly(boolean listEntriesOnly) {
        boolean oldValue = this.listEntriesOnly;
        this.listEntriesOnly = listEntriesOnly;
        this.firePropertyChange("listEntriesOnly", oldValue, listEntriesOnly);
    }

  /**
   * @return if blank entries are allowed.  Only used if listEntriesOnly is set to true.
   */
  public boolean getAllowBlankEntries() {
    return allowBlankEntries;
  }
  /**
   * @param allowBlankEntries Set to true if you set listEntriesOnly to true, however, you also want blank entries to be allowed
   */
  public void setAllowBlankEntries(boolean allowBlankEntries) {
    this.allowBlankEntries = allowBlankEntries;
  }

  public boolean isShowDropdowns() {
        return showDropdowns;
    }

    public void setShowDropdowns(boolean showDropdowns) {
        boolean oldValue = this.showDropdowns;
        this.showDropdowns = showDropdowns;
        this.firePropertyChange("showDropdowns", oldValue, showDropdowns);
    }

    @Override
    public void setName(String name) {
        super.setName(name);
        this.editor.getEditorComponent().setName(name + ".editor");
    }

    /**
     * Set whether this combobox should validate on each keystroke, that is whether it should fire
     * an AfterValueChage event for each key pressed by the end user or not
     * @param b True if AfterValueChange should be fired for each keystroke the user types
     */
    public void setValidateOnKeystroke(boolean b) {
        boolean oldValue = this.validateOnKeystroke;
        this.validateOnKeystroke = b;
        this.firePropertyChange("validateOnKeystroke", oldValue, b);
    }

    public boolean isValidateOnKeystroke() {
        return this.validateOnKeystroke;
    }

    /**
     * Set the maximum number of characters the fillin-field can accept.
     * @param maxCharacters
     */
    public void setMaxCharacters(int pMaxCharacters) {
        int oldValue = this.maxCharacters;
        this.maxCharacters = pMaxCharacters;
        this.firePropertyChange("maxCharacters", oldValue, this.maxCharacters);
        JTextComponent editorComp = (JTextComponent)editor.getEditorComponent();
        if (editorComp.getDocument() instanceof FixedLengthDocument) {
            ((FixedLengthDocument)(editorComp.getDocument())).setMaxLength(pMaxCharacters);
        }
    }

    public int getMaxCharacters() {
        return this.maxCharacters;
    }

    public void setBackground(Color color){
        super.setBackground(color);
        if (this.editor != null && this.editor.getEditorComponent() != null)
            this.editor.getEditorComponent().setBackground(color);
    }

    public void requestFocus() {
        if (this.editor != null && this.editor.getEditorComponent() != null) {
            this.editor.getEditorComponent().requestFocus();
        }
        else {
            super.requestFocus();
        }
    }

    public void postBeforeFocusLoss() {
        if (this.editor != null && this.editor instanceof BorderedComboBoxEditor) {
            ((BorderedComboBoxEditor)this.editor).postBeforeFocusLoss();
        }
    }

    public void postAfterFocusGain() {
        if (this.editor != null && this.editor instanceof BorderedComboBoxEditor) {
            ((BorderedComboBoxEditor)this.editor).postAfterFocusGain();
        }
    }

    public void postAfterValueChange() {
        if (this.editor != null && this.editor instanceof BorderedComboBoxEditor) {
            ((BorderedComboBoxEditor)this.editor).postAfterValueChange();
        }
    }

    public FillInField cloneComponent(){
        FillInField clone = DropFillinFactory.newFillinField(getElementList());
        CloneHelper.cloneComponent(this, clone, new String[] {"IntegerValue", "TextValue", "ObjectValue", "parent"});
        return clone;
    }

    /**
     * Reset the expected state of the combobox to the passed value. This method is public by an implementaation consequence only
     * and should not be called by non-helper class code.
     * @param pValue
     */
    public void initialise(String pValue) {
      this.oldText = pValue;
    }

    /**
     * An editor for the combo box that indents the values just a little to get it looking better. This also
     * handles the posting of events in response to focus gain / loss and keyboard events
     */
    public class BorderedComboBoxEditor extends BasicComboBoxEditor implements FocusListener {
        protected boolean hasKeyBeenPressed = false;
        protected boolean allowFocusChange = true;
        protected boolean postEvents = true;
        protected boolean purging = false;

        public class Editor extends JTextField {
            public Editor() {
                super("",9);
            }
            @Override
            public void paste() {
                super.paste();
                handleKeyStroke();
            }
            @Override
            public void cut() {
                super.cut();
                handleKeyStroke();
            }
           
            /*
             * CraigM:22/01/2009 - Update our oldText attribute if data binding occurs.
             */
            @Override
            public void setText(String t) {
              super.setText(t);
             
              if (EventManager.isPostingEnabled() == false) {
                FillInField.this.oldText = t;
              }
            }
           
            @Override
            protected void processFocusEvent(FocusEvent e) {
                if (!postEvents){
                    postEvents = true;
                    if (allowFocusChange) {
                        super.processFocusEvent(e);
                        allowFocusChange = false;
                    }
                    return;
                }

                // it is possible for the super focus event to call back to ourselves, possibly altering
                // the state our our firstKey property. Hence, we temporarily remember this value
                boolean oldFirstKey = hasKeyBeenPressed;
                super.processFocusEvent(e);
                hasKeyBeenPressed = oldFirstKey;
                BorderedComboBoxEditor.this.handleFocusEvent(e);
            }

            public FillInField getFillInField() {
                return FillInField.this;
            }
        }
        public BorderedComboBoxEditor() {
            super.editor = new Editor();
            super.editor.setBorder(BorderFactory.createEmptyBorder(0, DataField.LEFT_PAD, 0, 0));
            super.editor.addFocusListener(this);
            super.editor.addKeyListener(new KeyAdapter() {
              @Override
              public void keyPressed(KeyEvent e) {
                    int code = e.getKeyCode();     
                    if(code != KeyEvent.VK_UNDEFINED) {
                        if (e.getID() == KeyEvent.KEY_PRESSED) {
                            if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_DELETE) {
                                handleKeyStroke();
                            }
                        }
                    }
              }
                @Override
                public void keyTyped(KeyEvent e) {
                    super.keyTyped(e);
                    int code = e.getKeyCode();     
                    char ch = e.getKeyChar();
                    // Focus must be in the field to get the keystroke, so we can't be purging
                    //this.resetPurgingFlag();

                    // TF:11/11/2009:DET-126:This code is not triggered on the KeyTyped as the character code is
                    // not present. Moved it to the KeyPressed override.
                    if(code != KeyEvent.VK_UNDEFINED) {
//                        if (e.getID() == KeyEvent.KEY_PRESSED) {
//                            if (code == KeyEvent.VK_BACK_SPACE || code == KeyEvent.VK_DELETE) {
//                                handleKeyStroke();
//                            }
//                        }
                    }
                    else {
                        boolean accepted =  !Character.isIdentifierIgnorable(ch) &&
                                            !e.isAltDown() && !e.isActionKey() &&
                                            !e.isAltGraphDown() && !e.isMetaDown() &&
                                            !e.isControlDown() && !e.isConsumed();

                        if(accepted){          
                            handleKeyStroke();
                        }
                    }

                    // CraigM:22/01/2009 - The enter key should fire a default button, so create a click event if one exists
                    if (ch == KeyEvent.VK_ENTER) {
                      JButton defaultBtn = FillInField.this.getRootPane().getDefaultButton();
                     
                      if (defaultBtn != null) {
                        FocusHelper.buttonClick(
                            defaultBtn,
                            new EventHandle(defaultBtn, "Click", ClickListener.makeParamList(e)));
                      }
                    }
                }
            });
        }

        public Component getEditorComponent() {
            return super.editor;
        }


        private void resetPurgingFlag() {
            if (this.purging) {
                this.purging = false;
                this.hasKeyBeenPressed = true;
            }
        }

        public void disableEvents(){
            this.postEvents = false;
        }
        public void disableEvents(boolean pAllowFocusChange) {
            this.disableEvents();
            allowFocusChange = pAllowFocusChange;
        }

        protected void handleFocusEvent(FocusEvent e) {

            if (e.getID() == FocusEvent.FOCUS_GAINED) {
              this.gainFocus();
             
                //Let parent know about focus gain
                if (getParent() != null && getParent() instanceof JTable){
                    ((ArrayFieldModel)((JTable)getParent()).getModel()).focusGained(e);
                }
                // TF:9/10/07:Added the selection of all the text on focus gain to mimic the Forte behaviour
                this.selectAll();
            }
            if (e.getID() == FocusEvent.FOCUS_LOST) {
                // TF:If we're an editor of an array field, don't post this as the array field will do it explicitly
                if (ArrayFieldCellHelper.getArrayField(FillInField.this) == null) {
                    this.loseFocus(e);
                }
             
                //Let parent know about focus loss
                if (getParent() != null && getParent() instanceof JTable)
                    ((ArrayFieldModel)((JTable)getParent()).getModel()).focusLost(e);
            }
        }

        public void gainFocus() {
            // If the field is currently in error, and the user tabs or clicks away from it, we'll
            // lose the focus, validate, display the error message and then return to the field.
            // Unfortunately, this returning to the field generates a FOCUS_GAINED event, and we
            // definitely don't want to reset the firstKey flag (otherwise the verification passes
            // next time)
            if  (!this.purging) {
                this.hasKeyBeenPressed = false;
            }
            else {
                // Only purge the first focus gain event
                this.resetPurgingFlag();
            }
            this.postAfterFocusGain();
        }

        public void loseFocus(FocusEvent e) {
            // We're definitely in the field, so we can't be purging
            this.resetPurgingFlag();
            if (hasKeyBeenPressed && !validateOnKeystroke) {
              // CraigM:22/01/2009 - Call the generic postAfterValueChange method
                // Hashtable<String, Object> params = createParams();
                // ClientEventManager.postEvent( FillInField.this, "AfterValueChange", params);
                this.postAfterValueChange();

            }
           
            // DK:19/12/2008:Do not post BeforeFocusLost if focus just moved inside of the same FillInField
            if (postEvents && (e == null || e.getOppositeComponent() != FillInField.this)) {
                this.postBeforeFocusLoss();
            }
        }
       
        public void postAfterValueChange() {
          // CraigM:22/01/2009 - Check if the value actually changed first (Java will post regardless)
          if (oldText.equals(FillInField.this.getTextValue().asString()) == false) {
              EventManager.startEventChain();
              Hashtable<String, Object> params = createParams();
              UIutils.setDataChangedFlag(FillInField.this);
              ClientEventManager.postEvent( FillInField.this, "AfterValueChange", params);
              // TF:27/9/07:Revamped to use new event poster
              ChildEventHelper.postEventToAllParents(FillInField.this, "ChildAfterValueChange");
              oldText = FillInField.this.getTextValue().asString();
              EventManager.endEventChain();
          }
        }

        public void postAfterFocusGain() {
            EventManager.startEventChain();
            int reason = ForteKeyboardFocusManager.getTraversalReason();
            if (reason != Constants.FC_SUPRESS) {
                Hashtable<String, Object> params = createParams();
                params.put("reason", new ParameterHolder(reason));
                ClientEventManager.postEvent( FillInField.this, "AfterFocusGain", params );
            }
            EventManager.endEventChain();
        }
        public void postBeforeFocusLoss() {
            EventManager.startEventChain();
            FocusHelper.addSetFocusPurgeAction(FillInField.this);
//                FocusHelper.addPurgeAction(new RollbackAction());
            Hashtable<String, Object> params = createParams();
            int reason = ForteKeyboardFocusManager.getTraversalReason();
            if (reason != Constants.FC_SUPRESS) {
                params.put("reason", new ParameterHolder(reason));
                ClientEventManager.postEvent( FillInField.this, "BeforeFocusLoss", params );
            }
            EventManager.endEventChain();
        }

        public void handleKeyStroke() {
            if (validateOnKeystroke) {
                // We need to post AfterValueChange on each keystroke
                postAfterValueChange();
            }
            else {
                if (!hasKeyBeenPressed) {
                    // First key stroke, let's post the firstKeyStroke
                    Hashtable<String, Object> params = createParams();
                    ClientEventManager.postEvent( FillInField.this, "AfterFirstKeystroke", params );
                    // TF:27/9/07:Revamped to use new event poster
                    ChildEventHelper.postEventToAllParents(FillInField.this, "ChildAfterFirstKeystroke", params);
                    hasKeyBeenPressed = true;
                }
            }
        }

        protected Hashtable<String, Object> createParams(){
            Hashtable<String, Object> params = new Hashtable<String, Object>();
            JTable table = ArrayFieldCellHelper.getArrayField(FillInField.this);
            if (table != null){
                // params.put( "row", new ParameterHolder(TableRow) );
                // params.put( "column", new ParameterHolder(TableColumn) );
                params.put( "ArrayField", new ParameterHolder(table) );
            }
            return params;
        }

    }

    /**
     * A renderer for the combo box that indents the values a little too
     */
    public static class BorderedComboBoxRenderer extends BasicComboBoxRenderer {
        public BorderedComboBoxRenderer() {
            super();
            this.setBorder(new EmptyBorder(0, DataField.LEFT_PAD, 0, 0));
        }
    }

    /**
     * Inner class to handle the mapping of fillin-fields directly onto a list of values (ie autocomplete the fill-in field)
     * @author tfaulkes
     *
     */
    protected static class FillinManagementDocument extends PlainDocument{
        protected FillInField comboBox;
        protected ComboBoxModel model;
        protected JTextComponent editor;
        protected boolean showDropdowns;
        protected boolean strictValidation;
        protected int maxCharacters = 0;

        // Flag to indicate if setSelectedItem has been called.
        // Subsequent calls to remove/insertString should be ignored.
        protected boolean selecting = false;
        protected boolean hidePopupOnFocusLoss;
        protected boolean hitBackspace = false;
        protected boolean hitBackspaceOnSelection;

        private KeyListener editorKeyListener;
        private FocusListener editorFocusListener;
        private ActionListener actionListener;
        private PropertyChangeListener propertyChangeListener;

        public FillinManagementDocument(final FillInField comboBox, boolean pShowDropdowns, boolean pListEntriesOnly) {
            this(comboBox, pShowDropdowns, pListEntriesOnly, 0);
        }

        public FillinManagementDocument(final FillInField comboBox, boolean pShowDropdowns, boolean pListEntriesOnly, int pMaxCharacters) {
            super();
            this.comboBox = comboBox;
            this.model = comboBox.getModel();
            this.showDropdowns = pShowDropdowns;
            this.strictValidation = pListEntriesOnly;
            this.maxCharacters = pMaxCharacters;

            actionListener = new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (!selecting) highlightCompletedText(0);
                }
            };

            propertyChangeListener = new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent e) {
                    if (e.getPropertyName().equals("editor")) configureEditor((ComboBoxEditor) e.getNewValue());
                    else if (e.getPropertyName().equals("model")) model = (ComboBoxModel) e.getNewValue();
                    else if (e.getPropertyName().equals("maxCharacters")) maxCharacters = ((Integer)e.getNewValue()).intValue();
                    else if (e.getPropertyName().equals("showDropdowns")) showDropdowns = ((Boolean)e.getNewValue()).booleanValue();
                    else if (e.getPropertyName().equals("listEntriesOnly")) strictValidation = ((Boolean)e.getNewValue()).booleanValue();
                }
            };

            editorKeyListener = new KeyAdapter() {
                public void keyPressed(KeyEvent e) {
                    if (comboBox.isDisplayable() && showDropdowns) {
                        Object item = null;
                        try {
                            item = lookupItem(getText(0, getLength()));
                        }
                        catch (BadLocationException err) {}
                        if (strictValidation || item != null) {
                            // If this is a request to display the menu (F4) then don't show it again as this will actually hide it
                            if (e.getKeyCode() != KeyEvent.VK_F4) {
                                comboBox.setPopupVisible(true);
                            }
                        }
                    }
                    hitBackspace = false;
                    switch (e.getKeyCode()) {
                        // determine if the pressed key is backspace (needed by the
                        // remove method)
                        case KeyEvent.VK_BACK_SPACE:
                            hitBackspace = true;
                            hitBackspaceOnSelection = editor.getSelectionStart() != editor.getSelectionEnd();
                           
                            // CraigM:07/07/2008 - If we are allowed blank entries
                            if (comboBox.getAllowBlankEntries() && editor.getSelectionStart() < 2) {
                              FillinManagementDocument.this.setText("");
                            }
                            break;
                        // ignore delete key
                        case KeyEvent.VK_DELETE:
                            // CraigM:07/07/2008 - If we are allowed blank entries
                            if (comboBox.getAllowBlankEntries() && editor.getSelectionStart() < 2) {
                              FillinManagementDocument.this.setText("");
                            }
                            else if (strictValidation) {
                                e.consume();
                                comboBox.getToolkit().beep();
                            }
                            break;
                        case KeyEvent.VK_ENTER:
                          // CraigM:11/07/2008 - The enter key should fire a default button, so redispatch the event
                          FillinManagementDocument.this.comboBox.getParent().dispatchEvent(e);
                          break;
                    }
                }
            };

            // Bug 5100422 on Java 1.5: Editable JComboBox won't hide popup when tabbing out
            hidePopupOnFocusLoss = System.getProperty("java.version").startsWith("1.5");
            // Highlight whole text when gaining focus
            editorFocusListener = new FocusAdapter() {
                public void focusGained(FocusEvent e) {
                    highlightCompletedText(0);
                }

                public void focusLost(FocusEvent e) {
                    // Workaround for Bug 5100422 - Hide Popup on focus loss
                    if (hidePopupOnFocusLoss) comboBox.setPopupVisible(false);
                }
            };

            configureEditor(comboBox.getEditor());
            this.install();

            // Handle initially selected object
            Object selected = comboBox.getSelectedItem();
            if (selected!=null) {
                setText(selected.toString());
            }
            highlightCompletedText(0);
        }

        void configureEditor(ComboBoxEditor newEditor) {
            if (editor != null) {
                editor.removeKeyListener(editorKeyListener);
                editor.removeFocusListener(editorFocusListener);
            }

            if (newEditor != null) {
                editor = (JTextComponent)newEditor.getEditorComponent();
                editor.addKeyListener(editorKeyListener);
                editor.addFocusListener(editorFocusListener);
                editor.setDocument(this);
            }
        }

        private void install() {
            this.comboBox.addActionListener(this.actionListener);
            this.comboBox.addPropertyChangeListener(this.propertyChangeListener);
        }

        public void uninstall() {
            this.configureEditor(null);
            this.comboBox.removePropertyChangeListener(this.propertyChangeListener);
            this.comboBox.removeActionListener(this.actionListener);
            this.editor = null;
            this.comboBox = null;
        }

        @Override
        public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
            if (selecting) return;
            if (strictValidation) {
                super.replace(offset, length, text, attrs);
            }
            else {
                StringBuilder buf = new StringBuilder(getText(0, getLength()));
                buf.replace(offset, offset + length, text);
                if (maxCharacters > 0 && buf.length() > maxCharacters) {
                    // Too long, express disapproval!
                    Toolkit.getDefaultToolkit().beep();
                }
                else {
                    setText(buf.toString());
                    nonStrictEdit();
                }
            }
        }

        public void remove(int offs, int len) throws BadLocationException {
            // return immediately when selecting an item
            if (selecting) return;
            if (strictValidation) {
                if (hitBackspace) {
                    // user hit backspace => move the selection backwards
                    // old item keeps being selected
                    if (offs>0) {
                        if (hitBackspaceOnSelection) offs--;
                    } else {
                        // User hit backspace with the cursor positioned on the start => beep
                        comboBox.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
                    }
                    highlightCompletedText(offs);
                } else {
                    super.remove(offs, len);
                }
            }
            else {
                if (hitBackspace) {
                    // user hit backspace => move the selection backwards
                    // old item keeps being selected
                    if (offs>0) {
                        if (hitBackspaceOnSelection) offs--;
                    }
                    setText(getText(0, offs));
                    nonStrictEdit();
//                    highlightCompletedText(offs);
                } else {
                    super.remove(offs, len);
                }
//                super.remove(offs, len);
//                nonStrictEdit();
//                int length = getLength();
//                Object item = lookupItem(getText(0, length));
//                if (item != null) {
//                    setSelectedItem(item);
//                    setText(item.toString());
//                    highlightCompletedText(length);
//                }
            }
        }

        private void nonStrictEdit() throws BadLocationException {
            int length = getLength();
            Object item = lookupItem(getText(0, length));
            if (item != null) {
                setSelectedItem(item);
                setText(item.toString());
                highlightCompletedText(length);
                if (showDropdowns) {
                    comboBox.setPopupVisible(true);
                }
            }
            else {
                comboBox.setPopupVisible(false);
            }

        }
        public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
            if (selecting || str == null) return;
            if (!strictValidation && maxCharacters > 0 && getLength() + str.length() > maxCharacters) {
                // This is too long, so just beep and return
                Toolkit.getDefaultToolkit().beep();
                return;
            }
            // insert the string into the document
            super.insertString(offs, str, a);
            // lookup and select a matching item
            Object item = lookupItem(getText(0, getLength()));
            if (strictValidation) {
                if (item != null) {
                    setSelectedItem(item);
                }
                else {
                    // keep old item selected if there is no match
                    item = comboBox.getSelectedItem();
                    // imitate no insert (later on offs will be incremented by str.length(): selection won't move forward)
                    offs = offs-str.length();
                    // provide feedback to the user that his input has been received but can not be accepted
                    comboBox.getToolkit().beep(); // when available use: UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
                }
                if ((item != null) && (offs > -1)) {    //GK: added check for null condition
                  setText(item.toString());
                    // select the completed part
                    highlightCompletedText(offs+str.length());
                }
                else {
                  setText("");
                }
            }
            else {

                if (item != null) {
                    setSelectedItem(item);
                    setText(item.toString());
                    // select the completed part
                    highlightCompletedText(offs+str.length());
                }
                else {
                    comboBox.setPopupVisible(false);
                }
            }
        }

        private void setText(String text) {
            try {
                // remove all text and insert the completed string
                super.remove(0, getLength());
                super.insertString(0, text, null);
            } catch (BadLocationException e) {
                RuntimeException errorVar = new RuntimeException(e.toString(), e);
                ErrorMgr.addError(errorVar);
                throw errorVar;
            }
        }

        private void highlightCompletedText(int start) {
            editor.setCaretPosition(getLength());
            editor.moveCaretPosition(start);
        }

        private void setSelectedItem(Object item) {
            selecting = true;
            model.setSelectedItem(item);
            selecting = false;
        }

        private Object lookupItem(String pattern) {
          // DK:16/09/2008: if pattern if empty that means no text was entered, so return null
          if (pattern != null && pattern.length() == 0) {
        return null;
      }
            Object selectedItem = this.model.getSelectedItem();
            // only search for a different item if the currently selected does not match
            if (selectedItem != null && startsWithIgnoreCase(selectedItem.toString(), pattern)) {
                return selectedItem;
            } else {
                // iterate over all items
                for (int i=0, n=model.getSize(); i < n; i++) {
                    Object currentItem = model.getElementAt(i);
                    // current item starts with the pattern?
                    if (currentItem != null && startsWithIgnoreCase(currentItem.toString(), pattern)) {
                      // CraigM:07/07/2008 - Set the selected item so events will be fired notifying of a value change
                      this.comboBox.setSelectedItem(currentItem);
                        return currentItem;
                    }
                }
            }
            // no item starts with the pattern => return null
            return null;
        }

        // checks if str1 starts with str2 - ignores case
        private boolean startsWithIgnoreCase(String str1, String str2) {
            return str1.toUpperCase().startsWith(str2.toUpperCase());
        }
    }

    public int getVisibleColumns() {
        return visibleColumns;
    }

    public void setVisibleColumns(int visibleColumns) {
        this.visibleColumns = visibleColumns;
    }
   
    //PM:14/3/08 properties for binding the edited value
    public void setEditedValue(String value){
      setTextValue(new TextData(value));
    }
  //PM:14/3/08 properties for binding the edited value
    public String getEditedValue(){
      return getTextValue().toString();
    }
    //PM:11/08/07
    public class FillinFieldUI extends WindowsComboBoxUI{

        public Dimension getMinimumSize( JComponent c ) {
            if (WidthPolicy.get(c) == Constants.SP_EXPLICIT && ((FillInField)c).visibleColumns > 0){
                int colWidth = UIutils.colsToPixels(((FillInField)c).visibleColumns, c);
                if ( !isMinimumSizeDirty ) {
                    return new Dimension(cachedMinimumSize);
                }

                Dimension size = getDisplaySize();
                if (colWidth != size.width)
                    size.width = colWidth;
                Insets insets = getInsets();
                size.height += insets.top + insets.bottom;
                int buttonSize = size.height - (insets.top + insets.bottom);
                size.width +=  insets.left + insets.right + buttonSize;

                cachedMinimumSize.setSize( size.width, size.height );
                isMinimumSizeDirty = false;

                return new Dimension(size);
            } else {
                return super.getMinimumSize(c);
            }
        }
       
        @Override
        protected ComboPopup createPopup() {
          return new FillInFieldPopup(comboBox);
        }

        public class FillInFieldPopup extends BasicComboPopup {

      public FillInFieldPopup(JComboBox combo) {
        super(combo);
      }

      @Override
      public void show() {
        // DK:20/10/2008:if selected is not defined then scroll the list to the beginning of the list
        if (comboBox.getSelectedIndex() < 0 && list.getModel().getSize() > 0) {
            list.ensureIndexIsVisible(0);
        }
        super.show();
      }
     
        }
       
    }

    /**
     * There is a bug in Java that doesn't allow editable combo boxes to have their disabled colour set.
     * To get around this, we switch them to be not editable when they are in view only mode (ie: disabled but focusable).
     * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4335575">Bug Ref</a>
     * CraigM:30/04/2008.
     */
    private void fixForColours(FillInField fif) {
      if (UIManager.getLookAndFeel() instanceof Win32LookAndFeel) {
        // To show the black text, we must switch the fill in field to be not editable (so it is like a drop list)
        if (fif.isEnabled() == false && fif.isFocusable()) {
          fif.setEditable(false);
        }
        else {
          fif.setEditable(true);
        }
      }
    }

  /* CraigM:30/04/2008
   * @see java.awt.Component#setFocusable(boolean)
   */
  @Override
  public void setEnabled(boolean b) {
    super.setEnabled(b);
    this.fixForColours(this);
  }
 
  /* CraigM:30/04/2008
   * @see java.awt.Component#setFocusable(boolean)
   */
  @Override
  public void setFocusable(boolean focusable) {
    super.setFocusable(focusable);
    this.fixForColours(this);
  }

  /*
   * CraigM:30/04/2008 - If we are in view only mode (ie: disabled but focusable), leave our colour as black.
   *
   * @see javax.swing.JComponent#paint(java.awt.Graphics)
   */
  @Override
  public void paint(Graphics g) {
    if (UIManager.getLookAndFeel() instanceof Win32LookAndFeel && this.isEnabled() == false && this.isFocusable()) {
      Color foreground  = UIManager.getColor("ComboBox.disabledForeground");
      UIManager.getDefaults().put("ComboBox.disabledForeground", Color.black);
      super.paint(g);
      UIManager.getDefaults().put("ComboBox.disabledForeground", foreground);
    }
    else {
      super.paint(g);
    }
  }
 
  public void copy() {
    // TF:18/05/2009:Added this method to satisfy the CharacterField interface
    if (this.getEditor() instanceof JTextComponent) {
      ((JTextComponent)this.getEditor()).copy();
    }
  }
 
  public void cut() {
    // TF:18/05/2009:Added this method to satisfy the CharacterField interface
    if (this.getEditor() instanceof JTextComponent) {
      ((JTextComponent)this.getEditor()).cut();
    }
  }
 
  public void paste() {
    // TF:18/05/2009:Added this method to satisfy the CharacterField interface
    if (this.getEditor() instanceof JTextComponent) {
      ((JTextComponent)this.getEditor()).paste();
    }
  }
}
TOP

Related Classes of DisplayProject.controls.FillInField$FillinFieldUI$FillInFieldPopup

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.