Impedir a movimentação do cursor num JTextField

Olá pessoal,

Estou tentando fazer com que um JTextfield se mostre com o cursor sempre no final do texto, sem dar a opção de o usuário mudar a posição do cursor, no entanto, nao sei se existe um método adequado para fazer isso.

seriam mais ou menos assim: (obs: “|” = pos do cursor )

| // o sujeito nao digitou nada, o cursor ta na pos = 0;
t| // o sugeito digitou uma letra, o cursor vai para a pos = 1. Pressionar “LEFT ARROW” nao deve fazer nada. “BACKSPACE” apagaria a letra e voltaria pra pos = 0.
te| // o sugeito digitou mais uma letra, o cursor vai para a pos = 2. Pressionar “LEFT ARROW” nao deve fazer nada. “BACKSPACE” apagaria a letra e voltaria pra pos = 1.

se alguém puder me ajudar…

agradeço desde já.

achei o seguinte numa mailing list:

[quote]Segue abaixo um fragmento de código que bloqueia teclas não numéricas:

[code] JTNumeroSocio = new JTextField();
JTNumeroSocio.setBounds(70, 10, 50, 20);

JTNumeroSocio.addKeyListener(new KeyAdapter()
{
// Permite somente a digitação das teclas númericas
public void keyTyped(KeyEvent e)
{
char c = e.getKeyChar();
if (!(c >= ‘0’ && c <= ‘9’))
{
e.consume();
}

if (c == KeyEvent.VK_ENTER ||
c == KeyEvent.VK_TAB)
{
// pode fazer algo quando alguem entrar ENTER ou TAB
}

’ }
});[/code]
Falta adaptar para aceitar a tecla backspace.

Espero que ajude.[/quote]

é mais ou menos isso?

CUIDADO: O fragmento de código acima não bloqueia a digitação de números. Usar um KeyListener não é a solução para esse caso. Até pq, o usuário pode selecionar um número, copia-lo com o mouse, e cola-lo no JTextField, ainda usando o mouse. Nenhum KeyEvent será disparado no processo.

O correto, se você quiser bloquear digitação no JTextField, é usar um Document:
http://www.guj.com.br/article.show.logic?id=29

Para controlar o Caret (que é o que o autor original do tópico quer), provavemente você terá que registrar um caretListener e usar o comando setCaretPosition().

olá pessoal,

Eu tinha pensado em usar listener, mas tbém achei que nao seria a solução mais adequada, visto que eu teria que tratar vários eventos, porém agradeço a atenção.
Eu estava usando Document também… com ele eu consigo bloquear a inserção de chars no meio do campo, mas ainda sim dá pra posicionar o cursor lá.

Depois de muito pesquisar, vi que existe o NavigationFilter que serve exatamente pra isso: controlar a movimentação do cursor.
http://download.oracle.com/javase/1.5.0/docs/api/javax/swing/text/NavigationFilter.html

Segue abaixo a minha solução.

JMonetaryField.java:

package javax.swing.extras;

import javax.swing.JTextField;
import javax.swing.text.NavigationFilter;
import javax.swing.text.Position;

public class JMonetaryField extends JTextField {

    public JMonetaryField() {
        this("");
    }

    public JMonetaryField(String text) {
        super(text);
        setNavigationFilter(new MyNavigationFilter());
        setDocument(new MonetaryDocument());
    }

    @Override
    public final void setNavigationFilter(NavigationFilter filter) {
        if (filter == null || !filter.getClass().isAssignableFrom(MyNavigationFilter.class)) {
            throw new IllegalArgumentException("The navigation filter must be a instance of "
                    + "MyNavigationFilter and cannot be null.");
        }
        super.setNavigationFilter(filter);
    }

    protected final class MyNavigationFilter extends NavigationFilter {

        @Override
        public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
            super.setDot(fb, getText().length(), bias);
        }

        @Override
        public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
            super.moveDot(fb, getText().length(), bias);
        }
    }
}

MonetaryDocument.java:

package javax.swing.extras;

import java.text.DecimalFormatSymbols;
import java.util.Locale;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

public class MonetaryDocument extends PlainDocument {

    protected final DecimalFormatSymbols formatSymbols;

    public MonetaryDocument() {
        this(Locale.getDefault());
    }

    public MonetaryDocument(Locale locale) {
        super();
        formatSymbols = DecimalFormatSymbols.getInstance(locale);
    }

    @Override
    public void insertString(final int offset, final String str, final AttributeSet a) throws BadLocationException {
        if (str == null) {
            return;
        }
        final String toInsert = cleanString(str);
        if (toInsert.isEmpty()) {
            return;
        }
        final String oldValue = getText();
        int centIndex = oldValue.indexOf(formatSymbols.getDecimalSeparator());
        if (offset > centIndex) {
            remove(0, getLength());
            final String beforeCur = cleanString(oldValue.substring(0, offset));
            final String afterCur = (oldValue.substring(offset));

            StringBuffer strBuf = new StringBuffer(beforeCur).append(toInsert).append(afterCur);
            if (strBuf.length() < 3) {
                strBuf.insert(0, formatSymbols.getDecimalSeparator());
            } else {
                strBuf.insert(strBuf.length() - 2, formatSymbols.getDecimalSeparator());
            }
            super.insertString(0, strBuf.toString(), a);
        } else {
            super.insertString(offset, str, a);
        }
    }

    @Override
    public void remove(final int offset,
                       final int length) throws BadLocationException {
        String oldValue = getText();
        int centIndex = oldValue.indexOf(formatSymbols.getDecimalSeparator());
        if (offset == centIndex && length == 1) {
            // nao deixa remover o separador.
            return;
        } else if (offset + length > centIndex) {
            super.remove(offset, length);
            String texto = cleanString(getText());
            super.remove(0, getLength());
            insertString(0, texto, null);
        } else {
            super.remove(offset, length);
        }
    }

    private String getText() throws BadLocationException {
        return getText(0, getLength());
    }

    private String cleanString(String s) {
        return s.replaceAll("[^0-9]", "");
    }
}

PS: Na real, o que eu queria fazer era um TextField para valores monetários, para que a pessoa só digite os dígitos e o próprio campo coloque o separador de centavos automatcamente.
Ja vi vários exemplos no próprio GUJ e em outros locais, mas nenhum funcionava 100%. Geralmente qdo se insere ou remove chars ele distorce a posição do cursor, por causa dos chars removidos/adicionados programaticamente para casar com o formato.
Desse modo, resolvi fazer um TextField que só aceite o cursor no fim do mesmo.

abraços.

Legal, o NavigationFilter eu ainda não conhecia.

A única coisa que sei é que listeners geralmente não são o local adequado para esse tipo de tratamento. Antes de dizer para usar o evento do caret, até abri o javadoc para procurar algum CaretModel, ou coisa parecida. Como não achei, pensei que fosse a única opção disponível. Foi legal saber que existe mesmo uma opção.