001    /*
002     * Copyright 2002-2007 the original author or authors.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package org.springframework.richclient.text;
017    
018    import javax.swing.SwingUtilities;
019    import javax.swing.event.DocumentEvent;
020    import javax.swing.event.DocumentListener;
021    import javax.swing.text.JTextComponent;
022    
023    import org.springframework.binding.form.FormModel;
024    import org.springframework.richclient.form.builder.FormComponentInterceptor;
025    import org.springframework.richclient.form.builder.FormComponentInterceptorFactory;
026    
027    /**
028     * If the text is set in a text component, the caret position is set to the end of the
029     * text.
030     * <p>
031     * This means the beginning of the text will not be visible if the text is too long to fit
032     * in the text component.
033     * <p>
034     * This <code>FormComponentInterceptor</code> "fixes" this behaviour by listening to
035     * <code>Document</code> updates, and setting the caret position to 0 (i.e. the
036     * beginning of the text) if the text is updated when the text component doesn't have the
037     * focus (i.e. the text is not changed by the user).
038     * 
039     * @author Peter De Bruycker
040     */
041    public class TextCaretFormComponentInterceptorFactory implements FormComponentInterceptorFactory {
042    
043        /**
044         * Create a new <code>TextCaretFixerComponentInterceptor</code> instance
045         * 
046         * @return the interceptor
047         */
048        public FormComponentInterceptor getInterceptor( FormModel formModel ) {
049            return new TextCaretComponentInterceptor();
050        }
051    
052        /**
053         * The <code>FormComponentInterceptor</code> implementation.
054         */
055        public class TextCaretComponentInterceptor extends TextComponentInterceptor {
056            protected void processComponent( String propertyName, final JTextComponent textComponent ) {
057                textComponent.getDocument().addDocumentListener( new DocumentHandler( textComponent ) );
058            }
059        }
060    
061        private static final class DocumentHandler implements DocumentListener {
062            private JTextComponent component;
063    
064            private DocumentHandler( JTextComponent component ) {
065                this.component = component;
066            }
067    
068            public void removeUpdate( DocumentEvent e ) {
069                fixCaret();
070            }
071    
072            public void insertUpdate( DocumentEvent e ) {
073                fixCaret();
074            }
075    
076            public void changedUpdate( DocumentEvent e ) {
077                fixCaret();
078            }
079    
080            private void fixCaret() {
081                if( !component.hasFocus() ) {
082                    // need to invoke later, as the text change also changes the caret
083                    // position
084                    SwingUtilities.invokeLater( new Runnable() {
085                        public void run() {
086                            component.setCaretPosition( 0 );
087                        }
088                    } );
089                }
090            }
091        }
092    }