How can I replace a tab with four spaces in JTextArea? - java

I'm using a JTextArea in a JFrame. I would like the tab key to insert four spaces instead of a tab.
The method setTabSize does not work, as it puts a tab ('\t') in the contents of the text area.
How can I have JTextArea insert four spaces instead of a tab whenever I press the tab key? That way the getText() method will return indentations of four spaces for every tab.

I would avoid using KeyListeners (as a general rule with JTextComponents) and even Key Bindings, since while Key Bindings would work for keyboard input, it wouldn't work for copy-and-paste.
In my mind, the best way is to use a DocumentFilter set on the JTextArea's Document (which is a PlainDocument, by the way). This way, even if you copy and paste text into the JTextAreas, one with tabs, then all the tabs will automatically be converted to 4 spaces on insertion.
For example:
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;
public class TestTextArea {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JTextArea textArea = new JTextArea(20, 50);
JScrollPane scrollPane = new JScrollPane(textArea);
int spaceCount = 4;
((PlainDocument) textArea.getDocument()).setDocumentFilter(new ChangeTabToSpacesFilter(spaceCount));
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
private static class ChangeTabToSpacesFilter extends DocumentFilter {
private int spaceCount;
private String spaces = "";
public ChangeTabToSpacesFilter(int spaceCount) {
this.spaceCount = spaceCount;
for (int i = 0; i < spaceCount; i++) {
spaces += " ";
}
}
#Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
throws BadLocationException {
string = string.replace("\t", spaces);
super.insertString(fb, offset, string, attr);
}
#Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
super.remove(fb, offset, length);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
throws BadLocationException {
text = text.replace("\t", spaces);
super.replace(fb, offset, length, text, attrs);
}
}
}
So now, even if I copy and paste a document with tabs within it, into the JTextArea, all tabs will be automatically replaced with spaceCount spaces.

This is one of those “I wonder if…” moments.
Personally, I’d try a tackle the problem more directly, at the source. This means “trapping” the Tab event some how and “replacing” it’s functionality.
So, I started by modifying http://www.java2s.com/Tutorial/Java/0260__Swing-Event/ListingtheKeyBindingsinaComponent.htm, which could list the key bindings for component, for this, I found that the JTextArea was using insert-tab as the action map key.
I then created my own Action designed to insert spaces at the current caret position, for example…
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
// insert-tab
JTextArea ta = new JTextArea(10, 20);
add(new JScrollPane(ta));
ActionMap am = ta.getActionMap();
am.put("insert-tab", new SpacesTabAction());
}
public class SpacesTabAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
if (!(e.getSource() instanceof JTextArea)) {
return;
}
JTextArea textArea = (JTextArea) e.getSource();
int caretPosition = textArea.getCaretPosition();
Document document = textArea.getDocument();
try {
document.insertString(caretPosition, " ", null);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
}
}
}
Admittable limitations
This will not cover pasting the text into the component, which would otherwise be covered by a DocumentFilter, but, I like to think about scenarios where a DocumentFilter might not be usable (such as having one already installed)
More investigations
The method setTabSize does not work, as it puts a tab ('\t') in the contents of the text area.
Is this one of those “spaces vs tabs” flame wars :P
I did some fiddling and discovered that, most of the inconsistencies with setTabSize came about from not using a fixed width font, for example…
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
Font font = Font.decode("Courier New").deriveFont(12);
JTextArea ta = new JTextArea(10, 40);
ta.setFont(font);
ta.setText("---|----|----|----|----|----|----|----|\n\tHello");
ta.setTabSize(0);
add(new JScrollPane(ta));
DefaultComboBoxModel<Integer> model = new DefaultComboBoxModel<>(new Integer[] {0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24});
JComboBox<Integer> tabSizes = new JComboBox<>(model);
add(tabSizes, BorderLayout.NORTH);
tabSizes.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Integer tabSize = (Integer)tabSizes.getSelectedItem();
ta.setTabSize(tabSize);
}
});
}
}
}
When I set the font to “Courier New”, I was able to get a consistent indentation which was in align with the set tab size
I'm obviously missing something...
The tab is four spaces. In this "111\t" string, the tab is expands to 1 space; "22\t" — to 2 spaces; "3\t" — to 3 spaces; finally, "\t" expands to four spaces.
Yes, isn't this the expected behaviour?!
JTextArea, tabSize of 4, mono spaced font
Sublime text editor, tabSize of 4, mono spaced font
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.util.StringJoiner;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
StringJoiner textJoiner = new StringJoiner("\n");
for (int row = 0; row < 10; row++) {
StringJoiner rowJoiner = new StringJoiner("\t");
for (int col = 0; col < 10; col++) {
rowJoiner.add(Integer.toString(col).repeat(row));
}
textJoiner.add(rowJoiner.toString());
}
setLayout(new BorderLayout());
JTextArea ta = new JTextArea(10, 40);
ta.setFont(new Font("Monospaced", Font.PLAIN, 13));
ta.setText(textJoiner.toString());
add(new JScrollPane(ta));
}
protected String replicate(char value, int times) {
return new String(new char[times]).replace('\0', value);
}
}
}
I think you're thinking of "tab stops", rather than "tab size", for example, in which case, even the DocumentFilter won't do what you're expecting.
I assume the use of monospace font is implied in the question
Don't "assume" we know anything. In my experimentation, the font was NOT monospaced. This is where providing "expected" and "actual" results and a minimal reproducible example, as it removes the ambiguity and doesn't waste everybody's time (especially yours) 😉
It is how it works in any text editor.
I'm obviously using different text editors 🤪

Related

JTextArea. How can I replace text when printing new one?

I created JFrame and JTextArea above it. JTextArea has defaul text "This is text for demo version", that has been set via setText() method.
The goal is to implement this logic:
If I start printing text, the old text should be deleted and new one should apper.
After that, when new text is printed and if I click "Enter" - new text should be saved into private ArrayList<String> textList
The main question is How to replace old text when I print the first symbol of my text?
I tried to add TestTextArea.this.replaceRange(keyText,0, 30); inside keyReleased(KeyEvent e) {}; (30 it's the last index of default string "This is text for demo version" ). But everytime when I print anything, it causes IllegalArgumentException and seems that old text is still visible on the background.
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
public class TestTextArea extends JTextArea {
private String text = "This is text for demo version";
private ArrayList<String> textList = new ArrayList<>();
TestTextArea() {
setBackground(new Color(23, 28, 34, 240));
setForeground(new Color(6, 200, 109));
setCaretColor(new Color(6, 200, 109));
setCaretPosition(0);
setFont(new Font("Helvetica Neue", Font.BOLD, 16));
setText(text);
setLineWrap(true);
setWrapStyleWord(true);
setFocusable(true);
setEnabled(true);
setEditable(true);
setVisible(true);
addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
String keyText = KeyEvent.getKeyText(e.getKeyCode());
TestTextArea.this.replaceRange(keyText,0, 30);
if (keyText.equals("Enter")) {
textList.add(TestTextArea.this.getText());
}
}
});
}
public static void main(String []args) {
JFrame f = new JFrame();
TestTextArea area = new TestTextArea();
f.add(area);
f.setSize(400,200);
f.setDefaultCloseOperation(EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Is there any another way to solve this? I want that after I type the text and press Enter, all the text that I typed is saved. But at the moment it turns out that when you enter the first character, the default text is deleted, but even no one character is saved.
I am sorry, in advance for the possibly not very successful statement of the problem.
I think I understand your requirements.
When the first character is typed, clear the JTextArea and capture the typed text in the JTextArea,
When the Enter key is pressed, save the typed text in the List.
This code meets these requirements.
I use a JTextArea. The only reason you extend a Swing component, or any Java class, is to override one or more of the class methods.
I set the size of the JTextArea in the JTextArea constructor. Then I pack the JFrame.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class TextEntryExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new TextEntryExample());
}
private List<String> textList;
private String text;
private JTextArea textArea;
public TextEntryExample() {
this.text = "This is text for demo version";
this.textList = new ArrayList<>();
}
#Override
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.textArea = createPrompt();
f.add(textArea, BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private JTextArea createPrompt() {
JTextArea textArea = new JTextArea(10, 30);
textArea.addKeyListener(new PromptListener());
textArea.setBackground(new Color(23, 28, 34, 240));
textArea.setForeground(new Color(6, 200, 109));
textArea.setCaretColor(new Color(6, 200, 109));
textArea.setCaretPosition(0);
textArea.setFont(new Font("Helvetica Neue", Font.BOLD, 16));
textArea.setText(text);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
return textArea;
}
public class PromptListener implements KeyListener {
private boolean entry;
public PromptListener() {
this.entry = false;
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent event) {
if (!entry) {
String oldText = textArea.getText();
textArea.replaceRange("", 0, oldText.length());
entry = true;
}
}
#Override
public void keyReleased(KeyEvent event) {
if (entry) {
String keyText = KeyEvent.getKeyText(event.getKeyCode());
if (keyText.equals("Enter")) {
textList.add(textArea.getText());
entry = false;
}
}
}
}
}
This might not be the easiest, but you could replace the document with one that clears the text on the first edit.
I haven't checked, but I think JTextArea uses a DefaultStyledDocument. You can extend that and override the editing methods to check a flag - if it's set, clear the text and clear the flag. I've done something similar this for JTextField (using PlainDocument) - here's how that would look:
public class PromptDocument
extends PlainDocument
{
private bool clearOnEdit = false;
public void insertString(int offset, String str, AttributeSet a)
throws BadLocationException
{
if (clearOnEdit) {
super.remove(offset, getLength());
clearOnEdit = false;
}
super.insertString(offset, str, a);
}
public void remove(int offset, int len)
throws BadLocationException
{
if (clearOnEdit) {
super.remove(offset, getLength());
clearOnEdit = false;
} else {
super.remove(offset, len);
}
}
public void setClearOnEdit(final boolean clear) {
clearOnEdit = clear;
}
}
It should be similar for JTextArea (I haven't tested this, might be missing things). You can just use setDocument() on your JTextArea after you create it.

Java Swing Spell Checker

I have a quick question about implementing my SpellChecker into my Swing Text Editor. I tried to search around but couldn't find anything on how to get the red squiggly line under misspelled words. Is there something I could import then call on those misspellings? Also, how would I be able to make a menu pop up when I right-click on those miss spelled words? Thanks
There is plenty of material out there depending on what you want to do...
First of, use a JTextPane, which supports nice text formatting options.
How to underline text:
Comments are included as explanations.
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
public class WordUnderline {
public static void main(final String[] args) {
SwingUtilities.invokeLater(() -> {
final Style defaultStyle = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
final JTextPane pane = new JTextPane();
//My super-creative text...
pane.setText("These are words...\nHere come more words!\nWord word word.");
final StyledDocument doc = pane.getStyledDocument();
doc.addDocumentListener(new DocumentListener() {
private void clearStyle(final DocumentEvent e) {
SwingUtilities.invokeLater(() -> doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, true));
}
#Override
public void insertUpdate(final DocumentEvent e) {
//When you type a new letter, we want to (lets say) clear all the styles from the whole document...
clearStyle(e);
}
#Override
public void removeUpdate(final DocumentEvent e) {
//When you erase a letter, we want to (lets say) clear all styles from the whole document...
clearStyle(e);
}
#Override
public void changedUpdate(final DocumentEvent e) {
//When changing the style of the document, we want to do nothing else (but the change will happen).
}
});
final JButton doit = new JButton("Underline selected text!");
doit.addActionListener(e -> {
final SimpleAttributeSet sas = new SimpleAttributeSet();
StyleConstants.setUnderline(sas, true);
/*I would suggest here to experiment a bit with the StyleConstants
class... For example: StyleConstants.setBackground(sas, Color.RED);*/
final int start = pane.getSelectionStart();
final int end = pane.getSelectionEnd();
doc.setCharacterAttributes(start, end - start, sas, true);
});
final JPanel contents = new JPanel(new BorderLayout());
contents.add(doit, BorderLayout.PAGE_START);
contents.add(pane, BorderLayout.CENTER);
final JFrame frame = new JFrame("Word underline.");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(contents);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
How to pop a menu on word selection:
Comments are included as explanations.
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.StyledDocument;
import javax.swing.text.Utilities;
public class WordPopUp {
public static void main(final String[] args) {
SwingUtilities.invokeLater(() -> {
final JTextPane pane = new JTextPane();
//My super-creative text...
pane.setText("These are words...\nHere come more words!\nWord word word.");
pane.addMouseListener(new MouseAdapter() {
private boolean pendingPopUp = false; //Indicates whether we have already a popup popped up...
private void pop(final MouseEvent mevt) {
if (SwingUtilities.isRightMouseButton(mevt)) {
try {
final StyledDocument doc = pane.getStyledDocument();
//Get the location of the document where the user clicked:
final int offset = pane.viewToModel(mevt.getPoint());
//Find what word is at the location of the document where the user clicked:
final int start = Utilities.getWordStart(pane, offset),
end = Utilities.getWordEnd(pane, offset);
//Set the selection to be that word:
pane.setSelectionStart(start);
pane.setSelectionEnd(end);
//Obtain the value of the selected word:
final String word = doc.getText(start, end - start);
//Create the contents of the popup:
final JPanel popupPanel = new JPanel();
//Create the alternative words (via JButtons):
final int cnt = 4;
final ArrayList<JButton> words = new ArrayList<>();
for (int i = 0; i < cnt; ++i) {
final JButton button = new JButton(word + (i + 1));
popupPanel.add(button);
words.add(button);
}
final JButton cancel = new JButton("Cancel");
popupPanel.add(cancel);
//Create the popup itself:
final Popup popup = PopupFactory.getSharedInstance().getPopup(pane, popupPanel, mevt.getXOnScreen(), mevt.getYOnScreen());
//Hook action listenere to the word and cancel buttons:
words.forEach(button -> button.addActionListener(e -> {
try {
//Get the text of that button (it is going to be the new word):
final String newWord = ((JButton) e.getSource()).getText();
//Replace the old text with the new one:
doc.remove(start, end - start);
doc.insertString(start, newWord, null);
//Prepare caret position, so the user can keep on writing:
pane.setCaretPosition(start + newWord.length());
}
catch (final BadLocationException | RuntimeException x) {
JOptionPane.showMessageDialog(pane, "Oups!");
}
finally {
popup.hide();
pendingPopUp = false;
}
}));
//On cancel, deselect the selected text and close the popup:
cancel.addActionListener(e -> {
popup.hide();
pane.setSelectionStart(offset);
pane.setSelectionEnd(offset);
pendingPopUp = false;
});
pendingPopUp = true;
popup.show();
}
catch (final BadLocationException | RuntimeException x) {
JOptionPane.showMessageDialog(pane, "Oups! No word found?...");
}
}
}
private void maybePop(final MouseEvent mevt) {
if (mevt.isPopupTrigger()) {
if (pendingPopUp)
System.err.println("A popup is already popped. Close it to pop a new one.");
else
pop(mevt);
}
}
#Override
public void mouseClicked(final MouseEvent mevt) {
maybePop(mevt);
}
#Override
public void mousePressed(final MouseEvent mevt) {
maybePop(mevt);
}
#Override
public void mouseReleased(final MouseEvent mevt) {
maybePop(mevt);
}
});
final JFrame frame = new JFrame("Word underline.");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(pane));
//Give some room to spare:
final Dimension dim = frame.getPreferredSize();
dim.width += 100;
dim.height += 100;
frame.setPreferredSize(dim);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
Resources and references:
How to Use Editor Panes and Text Panes
clickable text from jTextPane
making text underline font by using JTextPane?
How to get selection from JTextPane
How to clear all styling from StyledDocument?
Getting 'Attempt to mutate notification' exception
How do I set different colors for text and underline in JTextPane?
Underline StyleConstant in a different colour with AttributeSet
Deselect selected text in JTextPane
how to get the red squiggly line under misspelled words.
You can highlight the text and use a custom Painter to draw the red squiggly line.
Check out the Squiggle Painter for the custom painting code.
Basic usage would be:
SquigglePainter red = new SquigglePainter( Color.RED );
try
{
textField.getHighlighter().addHighlight( ?, ?, red );
}
catch(BadLocationException ble) {}
where the "?" would be the start/end offset of the text you want to underline.

Java Swing TextArea: How to simultaneously print Transliterated text/s in an Output TextArea?

I am trying to write a text editor. I want two TextAreas: 1st one for typing/editing in unicode script and 2nd one for simultaneously printing output of the unicode script input in corresponding Roman Script (ASCII) set by myself on the basis of a transliteration scheme.
I am not able to update and simultaneously print the input text in output textarea while I call the transliteration method. I can sense a little bit that there is something wrong while I set the output textArea to print simultaneously but I am unable to find out what exactly is that.
The Editor class------->
import java.awt.*;
import java.beans.PropertyChangeSupport;
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
public class editor extends JPanel {
private static final int ROWS = 10;
private static final int COLS = 50;
private static final String[] BUTTON_NAMES = {"विविधाः", "द्वाराणि", "Setting", "Parse", "Compile"};
private static final int GAP = 3;
private JTextArea inputTextArea = new JTextArea(ROWS, COLS);
private JTextArea outputTextArea = new JTextArea(ROWS, COLS);
private JTextArea TA = new JTextArea(ROWS, COLS);
//calling the transliteration method
transliterator tr = new transliterator();
Document asciiDocument=tr.DevanagariTransliteration(inputTextArea.getDocument());
public editor() throws BadLocationException {
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, GAP, 0));
for (String btnName : BUTTON_NAMES) {
buttonPanel.add(new JButton(btnName));
}
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(buttonPanel);
add(putInTitledScrollPane(inputTextArea, "देवनागरी <<<<<<>>>>>> Devanagari"));
inputTextArea.setFocusable(true);
outputTextArea.setFocusable(false);
outputTextArea.setEditable(false);
//String inputCheck = inputTextArea.getText();
//inputTextArea.setText("x");
//if (inputTextArea.getText().length()>0) {
outputTextArea.setDocument(asciiDocument);//printing input in 2nd textarea
// outputTextArea.setDocument(inputTextArea.getDocument());//printing input in 2nd textarea
add(putInTitledScrollPane(outputTextArea, "IndicASCII"));
}
private JPanel putInTitledScrollPane(JComponent component,
String title) {
JPanel wrapperPanel = new JPanel(new BorderLayout());
wrapperPanel.setBorder(BorderFactory.createTitledBorder(title));
wrapperPanel.add(new JScrollPane(component));
return wrapperPanel;
}
private static void createAndShowGui() throws BadLocationException {
editor mainPanel = new editor();
JFrame frame = new JFrame("Unicode Editor");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
ImageIcon imgicon = new ImageIcon("MyIcon.jpg");
frame.setIconImage(imgicon.getImage());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
createAndShowGui();
} catch (BadLocationException e) {
e.printStackTrace();
}
}
});
}
}
And transliteration class method:
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
public class transliterator {
//method that returns a Document type
// the method - importing devanagari text through the type 'Document' from input textArea by call
public Document DevanagariTransliteration(Document devanagariTextDocument) throws BadLocationException {
//extracting the devanagari text from the imported Document type
String asciiText = devanagariTextDocument.getText(0, devanagariTextDocument.getLength());
//devanagari unicode a replaced by ascii a
String transliteratedText = asciiText.replace('\u0905', 'a');
JTextArea jt = new JTextArea();
//inserting the TRANSLITERATED text to a textArea to extract as a Document again
jt.setText(transliteratedText);
//extracting and creating as a document
Document ASCIITextDocument = jt.getDocument();
//returning the document
return ASCIITextDocument;
}
}
To reflect changes in one document onto another document you can use a DocumentListener as suggested by camickr.
The following is an mre that demonstrates updating the "output" JTextArea after processing the changes in the "input" JTextArea.
The processing used for demonstration purposes is a simply converting the input to upper case. This should be changed to your specific needs :
import java.awt.BorderLayout;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
public class MyWindow extends JPanel {
private static final int ROWS = 10, COLS = 50;
private final JTextArea outputTextArea;
public MyWindow() {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
JTextArea inputTextArea = new JTextArea(ROWS, COLS);
inputTextArea.getDocument().addDocumentListener(new TransliterateDocumentListener());
add(putInTitledScrollPane(inputTextArea,"Input"));
outputTextArea = new JTextArea(ROWS, COLS);
outputTextArea.setFocusable(false);
outputTextArea.setEditable(false);
add(putInTitledScrollPane(outputTextArea, "Output"));
}
private JPanel putInTitledScrollPane(JComponent component, String title) {
JPanel wrapperPanel = new JPanel(new BorderLayout());
wrapperPanel.setBorder(BorderFactory.createTitledBorder(title));
wrapperPanel.add(new JScrollPane(component));
return wrapperPanel;
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Document Listener Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add( new MyWindow());
frame.pack();
frame.setVisible(true);
}
private void insert(String text, int from) {
text = process(text);
try {
outputTextArea.getDocument().insertString(from, text, null);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
private void remove(int from, int length) {
try {
outputTextArea.getDocument().remove(from, length);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
private String process(String text) {
//todo process text as needed
//returns upper case text for demo
return text.toUpperCase();
}
class TransliterateDocumentListener implements DocumentListener {
#Override
public void insertUpdate(DocumentEvent e) {
Document doc = e.getDocument();
int from = e.getOffset(), length = e.getLength();
try {
insert(doc.getText(from, length), from);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
#Override
public void removeUpdate(DocumentEvent e) {
remove(e.getOffset(), e.getLength());
}
#Override
public void changedUpdate(DocumentEvent e) {
//Plain text components don't fire these events.
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
When I used the setDocument() method I cannot handle the text input
All the setDocument() method does is share the Document between multiple components. So whatever you type will be displayed in both components.
Instead you would use a DocumentListener. The listener will generate an event whenever text is added or removed from the Document. Then you will need to read the text from the Document and do your translation and update the second text area.
See the section from the Swing tutorial on How to Write a DocumentListener for the basics to get you started.

Java Swing JTextArea Line number

This count line number of textarea. The code works correctly, but when run this code the textarea is not active, the caret is hidden and the keyboard
keys not work unless I click on textarea.
code:
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Element;
public class LineNumber extends JFrame implements DocumentListener {
private static final long serialVersionUID = -1093726028044203117L;
private JScrollPane scroll;
private JTextArea textArea, lineArea;
public static void main(String[] args) {
new LineNumber().setVisible(true);
}
public LineNumber() {
super("Line Numbers");
setSize(500, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setUI();
}
private void setUI() {
textArea = new JTextArea();
lineArea = new JTextArea(0, 3);
lineArea.setEditable(false);
lineArea.setForeground(Color.GRAY);
scroll = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
textArea.getDocument().addDocumentListener(this);
scroll.setViewportView(textArea);
scroll.setRowHeaderView(lineArea);
getContentPane().add(scroll, BorderLayout.CENTER);
}
public void changedUpdate(DocumentEvent event) {
lineArea.setFont(textArea.getFont());
lineArea.setText(getLine());
}
public void insertUpdate(DocumentEvent event) {
lineArea.setFont(textArea.getFont());
lineArea.setText(getLine());
}
public void removeUpdate(DocumentEvent event) {
lineArea.setFont(textArea.getFont());
lineArea.setText(getLine());
}
public String getLine() {
int caretPos = 0;
String lines;
caretPos = textArea.getDocument().getLength();
Element root = textArea.getDocument().getDefaultRootElement();
lines = String.format("%s%s", 1, System.lineSeparator());
for (int i = 2; i < root.getElementIndex(caretPos) + 2; i++) {
lines += String.format("%s%s", i, System.lineSeparator());
}
return lines;
}
}
If I do not add lineArea to the scrollpane the textarea work correctly but after add to setRowHeaderView the textarea only gets active with a mouse click....
You can prevent the line number text area from gaining focus by using:
lineArea = new JTextArea(0, 3);
lineArea.setEditable(false);
lineArea.setFocusable(false);
You can also check out Text Component Line Number for a fancier implementation that supports:
wrapped text
text with different size fonts (when using a JTextPane)
By default it focus on first component, so if you want to focus on another one try this code in the constructor.
addWindowFocusListener(new WindowAdapter() {
#Override
public void windowGainedFocus(WindowEvent e) {
textArea.requestFocusInWindow();
}
});
textArea is now focused, more on this.

JTextField in which the predefined text in not editable but other text can be appended to it?

While going through Java swing I faced this problem.
I have a JTextField which has predefined and not editable text. the user should be able to append other text to it but without editing the predefined text. Is there any method to obtain this solution or any other?
You can simply use a DocumentFilter:
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
public class TestDocumentFilter {
private static final String TEXT_NOT_TO_TOUCH = "You can't touch this!";
private void initUI() {
JFrame frame = new JFrame(TestDocumentFilter.class.getSimpleName());
frame.setLayout(new FlowLayout());
final JTextField textfield = new JTextField(50);
textfield.setText(TEXT_NOT_TO_TOUCH);
((AbstractDocument) textfield.getDocument()).setDocumentFilter(new DocumentFilter() {
#Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
if (offset < TEXT_NOT_TO_TOUCH.length()) {
return;
}
super.insertString(fb, offset, string, attr);
}
#Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
if (offset < TEXT_NOT_TO_TOUCH.length()) {
length = Math.max(0, length - TEXT_NOT_TO_TOUCH.length());
offset = TEXT_NOT_TO_TOUCH.length();
}
super.replace(fb, offset, length, text, attrs);
}
#Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
if (offset < TEXT_NOT_TO_TOUCH.length()) {
length = Math.max(0, length + offset - TEXT_NOT_TO_TOUCH.length());
offset = TEXT_NOT_TO_TOUCH.length();
}
if (length > 0) {
super.remove(fb, offset, length);
}
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(textfield);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestDocumentFilter().initUI();
}
});
}
}
Take a look at DocumentFilter. It should allow you to define a "protected" area of text.
Examples here and here
I have a JTextField which has predefined and not editable text. the
user should be able to append other text to it but without editing the
predefined text. Is there any method to obtain this solution or any
other?
use
JComboBox (non_editable)
JSpinner with SpinnerListModel
originally made by #camickr
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class NavigationFilterPrefixWithBackspace extends NavigationFilter {
private int prefixLength;
private Action deletePrevious;
public NavigationFilterPrefixWithBackspace(int prefixLength, JTextComponent component) {
this.prefixLength = prefixLength;
deletePrevious = component.getActionMap().get("delete-previous");
component.getActionMap().put("delete-previous", new BackspaceAction());
component.setCaretPosition(prefixLength);
}
#Override
public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
fb.setDot(Math.max(dot, prefixLength), bias);
}
#Override
public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias) {
fb.moveDot(Math.max(dot, prefixLength), bias);
}
class BackspaceAction extends AbstractAction {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
JTextComponent component = (JTextComponent) e.getSource();
if (component.getCaretPosition() > prefixLength) {
deletePrevious.actionPerformed(null);
}
}
}
public static void main(String args[]) throws Exception {
JTextField textField = new JTextField(" $ ", 20);
textField.setNavigationFilter(new NavigationFilterPrefixWithBackspace(textField.getDocument().getLength(), textField));
JFrame frame = new JFrame("Navigation Filter Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(textField);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
I'd suggest to use OverlayLayout (JLabel over JTextField), by changing Insets (input area) in JTextField for JLabels area, otherwise any formatting in JTextField make code and suggestion in this thread quite useless and with strange output to the Swing GUI
e.g. JTextField.setHorizontalAlignment(JTextField.RIGHT);
EDIT
put JLabel & JTextField to JPanel, quite simple and without side effects
change built in FlowLayout for JPanel
required to call revalidate() and repaint() in the case that text in JLabel is changed
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class NavigationFilterBias {
private JFrame frame = new JFrame("Navigation Filter Example");
private JPanel panel = new JPanel();
private JLabel label = new JLabel(" $ ");
private JTextField textField = new JTextField();
public NavigationFilterBias() {
panel.setBorder(textField.getBorder());
panel.setBackground(textField.getBackground());
panel.setLayout(new BorderLayout());
panel.add(label,BorderLayout.WEST);
textField.setBorder(null);
panel.add(textField,BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String args[]) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
NavigationFilterBias exam = new NavigationFilterBias();
}
});
}
}
Though I believe DocumentFilter is the logical and versatile solution, a short solution here. It simply makes an JTextField with an inner left margin in which the fixed text is written.
public class PrefixTextField extends JTextField {
private String prefix;
private JLabel label;
public PrefixTextField(String prefix) {
this.prefix = prefix;
label = new JLabel(prefix + '\u00a0');
}
#Override
protected void paintComponent(Graphics g) {
int w = SwingUtilities.computeStringWidth(
getFontMetrics(getFont()), prefix);
setMargin(new Insets(3, 3 + w, 3, 3));
super.paintComponent(g);
SwingUtilities.paintComponent(g, label, this.getParent(),
2 + 3, 0, getWidth(), getHeight());
}
}
Alternative solution:
1. Put the $ symbole in the JTextField
2. Remove the dollar symbole when the JTextField get the focus
3. Let the user modify the full text
4. When he's done typing (see here) add the $ symbole back
But it would be way easier to add a label next to the JTextField

Categories

Resources