I have a button that inserts an unordered list item into a JTextPane. However, when I click on the button to insert a list item, two bullets are inserted instead of one. One bullet is inserted only during the first time insertion.
I cut out the functionality from my application and pasted the code into a small SSCCE (below) and the problem remains. Does anyone have any idea as to what might be happening here?
[The problem has been solved, below is the complete solved code. There are two ways to do this, refer to the functionality in the show and the bullets button]
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.ElementIterator;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
public class Main {
private static Button2 show = new Button2 ("Show");
private static LIButton bullets = new LIButton("Bullets", HTML.Tag.UL);
private static JEditorPane pane = new JEditorPane();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
create();
}
});
}
private static void create() throws HeadlessException {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pane.setPreferredSize(new Dimension(300, 300));
pane.setContentType("text/html");
frame.add(pane, BorderLayout.CENTER);
JPanel panel = new JPanel();
panel.add(bullets);
panel.add(show);
frame.add(panel, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
static class LIButton extends JButton {
static final String LI_HTML = "<HTML><BODY><UL><LI></LI></UL></BODY></HTML>";
public LIButton(String name, HTML.Tag parent) {
super(new HTMLEditorKit.InsertHTMLTextAction(
name, LI_HTML, HTML.Tag.UL, HTML.Tag.LI, HTML.Tag.BODY, HTML.Tag.UL));
}
}
static class Button2 extends JButton implements ActionListener {
static final String LI_HTML = "<HTML><BODY><UL><LI></LI></UL></BODY></HTML>";
public Button2(String name) {
super(name);
this.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent ae) {
HTMLDocument doc = (HTMLDocument) pane.getDocument();
HTMLEditorKit kit = (HTMLEditorKit) pane.getEditorKit();
try {
kit.insertHTML(doc, doc.getLength() - 1, LI_HTML, 0, 1, null);
} catch (BadLocationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The example below seems to work.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.HeadlessException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
public class Main {
private static LIButton bullets = new LIButton("Bullets", HTML.Tag.UL);
private static JTextPane pane = new JTextPane();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
create();
}
});
}
private static void create() throws HeadlessException {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pane.setPreferredSize(new Dimension(300, 300));
pane.setContentType("text/html");
pane.setText("<HTML><BODY><UL></UL></BODY></HTML>");
frame.add(pane, BorderLayout.CENTER);
JPanel panel = new JPanel();
panel.add(bullets);
frame.add(panel, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
static class LIButton extends JButton {
static final String LI_HTML = "<LI>item</LI>";
public LIButton(String name, HTML.Tag parent) {
super(new HTMLEditorKit.InsertHTMLTextAction(
name, LI_HTML, parent, HTML.Tag.LI));
}
}
}
Related
Below is a shortened version of my code to illustrate the problem. I was hoping scrollRectToVisible would move the scrollbar and scrollpane back to the top but it remains at the bottom. Thanks in advance for any suggestions.
package testing;
import javax.swing.SwingUtilities;
public class Testing {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new GUItest();
}
});
}
}
package testing;
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
public class GUItest extends JFrame {
private JEditorPane myEditorPane;
private JScrollPane myScrollPane;
public GUItest() {
myEditorPane = new JEditorPane();
myScrollPane = new JScrollPane(myEditorPane);
myScrollPane.setPreferredSize(new Dimension(400, 200));
getContentPane().add(myScrollPane);
myEditorPane.setContentType("text/html");
myEditorPane.setText("<html>" + "test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>" + "</html>");
Rectangle rect = new Rectangle(1, 1, 1, 1);
myEditorPane.scrollRectToVisible(rect);
pack();
setVisible(true);
}
}
It works if you do the scrolling "in the next frame render cycle" after the JFrame is visible:
import javax.swing.SwingUtilities;
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
public class GUItest extends JFrame {
private JEditorPane myEditorPane;
private JScrollPane myScrollPane;
public GUItest(){
myEditorPane = new JEditorPane();
myScrollPane = new JScrollPane(myEditorPane);
myScrollPane.setPreferredSize(new Dimension(400, 100));
getContentPane().add(myScrollPane);
myEditorPane.setContentType("text/html");
myEditorPane.setText("<html>" + "test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>" + "</html>");
Rectangle rect = new Rectangle(1,1,1,1);
pack();
setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
myEditorPane.scrollRectToVisible(rect);
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new GUItest();
}
});
}
}
It looks like the JFrame has to be valid/visible to issue the scroll command.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 4 years ago.
I have a problem with ActionListener. If I use an anonymous class everything works good, if the MainFrame class implements ActionListener everything works good. But if I create an extern class and implement ActionListener I get NullPointerException.
How can I fix this problem using an extern class that implement ActionListener interface?
Thanks in advance!
This is the code:
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class App {
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new MainFrame("Hello world Swing");
frame.setSize(500, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
MainFrame code:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
public class MainFrame extends JFrame {
JTextArea textArea;
public MainFrame(String title) {
super(title);
Container c = getContentPane();
c.setLayout(new BorderLayout());
textArea = new JTextArea();
JButton button = new JButton("Click me!");
c.add(textArea, BorderLayout.CENTER);
c.add(button, BorderLayout.SOUTH);
button.addActionListener(new ListenApp());
}
}
ListenApp code:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
public class ListenApp implements ActionListener {
MainFrame frame;
#Override
public void actionPerformed(ActionEvent e) {
frame.textArea.append("Hello\n");
}
}
It's because in ListenApp the frame (field) you have isn't declared as the frame you created in your main method.
They way to make it work is to pass the frame into the listener as an argument in the listener's constructor. Take a look:
MainFrame class:
package test;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class MainFrame extends JFrame {
private JTextArea textArea; // private fields are always recommended.
public MainFrame(String title) {
super(title);
Container c = getContentPane();
c.setLayout(new BorderLayout());
textArea = new JTextArea();
JButton button = new JButton("Click me!");
c.add(textArea, BorderLayout.CENTER);
c.add(button, BorderLayout.SOUTH);
button.addActionListener(new ListenApp(this)); //pass the frame to the listener
}
public JTextArea getTextArea() // Must have access to the textArea aswell
{
return textArea;
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new MainFrame("Hello world Swing");
frame.setSize(500, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
ListenApp class:
package test;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ListenApp implements ActionListener {
private MainFrame frame;
public ListenApp(MainFrame frame) {
this.frame = frame;
}
#Override
public void actionPerformed(ActionEvent arg0) {
frame.getTextArea().append("i append something");
}
}
It is said in manual, that if child does not implement Scrollable, then JScrollPane rely on preferredSize properties of it's content.
Apparently this is not true for me. I am increasing preferred height, but JScrollPane does not feel or react on it.
package tests;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Try01_JScrollPane extends JFrame {
private static final long serialVersionUID = 4123186105171813186L;
private static final Logger log = LoggerFactory.getLogger(Try01_JScrollPane.class);
JPanel yellowPanel = new JPanel();
{
yellowPanel.setPreferredSize(new Dimension(200,50));
yellowPanel.setSize(new Dimension(200,50));
yellowPanel.setBackground(Color.yellow);
}
JScrollPane scrollPane = new JScrollPane(yellowPanel);
{
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
}
AbstractAction increaseAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
yellowPanel.setPreferredSize(new Dimension(yellowPanel.getPreferredSize().width, yellowPanel.getPreferredSize().height+100));
log.debug("preferred height is now {}", yellowPanel.getPreferredSize().height);
}
};
Timer increaseTimer = new Timer(1000, increaseAction);
{
setLayout(new BorderLayout());
add(scrollPane, BorderLayout.CENTER);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(200, 400);
setTitle("Try01_JScrollPane");
increaseTimer.start();
setVisible(true);
}
public static void main(String[] args) {
new Try01_JScrollPane();
}
}
JPanel is container and JComponent too, for any changes to JViewport you have to notify the JScrollPane:-)
.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Try01_JScrollPane extends JFrame {
private static final long serialVersionUID = 4123186105171813186L;
private JFrame frame = new JFrame("Try01_JScrollPane");
private JPanel yellowPanel = new JPanel();
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
{
yellowPanel.setBackground(Color.yellow);
}
private JScrollPane scrollPane = new JScrollPane(yellowPanel);
{
scrollPane.setPreferredSize(new Dimension(400, 300));
}
private AbstractAction increaseAction = new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
yellowPanel.setPreferredSize(
new Dimension(yellowPanel.getPreferredSize().width + 100,
yellowPanel.getPreferredSize().height + 100));
yellowPanel.revalidate();
yellowPanel.repaint();
}
};
private Timer increaseTimer = new Timer(1000, increaseAction);
public Try01_JScrollPane() {
frame.add(scrollPane, BorderLayout.CENTER);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocation(150, 150);
frame.setVisible(true);
increaseTimer.start();
increaseTimer.setRepeats(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Try01_JScrollPane();
}
});
}
}
The JScrollPane cuts a view port out of a backing content have a scroll pane layout. The part on getPreferredSize refers to this layout. It simply says that the JScrollPane / view port rectangle is not influenced by the backing content and vice versa: content is layed out with respect to their preferred size.
So a change of preferred size need a new layouting. More sence would be to:
initialize with a setPreferredSize.
afterwards call setSize to resize.
I have a JFrame and, inside this JFrame there are two JPanels. When I press a key, both of them must listen to this key event and act. I want to take all the keyboard events, and deliver them to both of the JPanels. Do you know how to do it?
Edit: Since they must do different things, I need two different listeners, sorry for not being specific.
Edit2: I made a simple code to show you the problem. When I press the up key, both of the JPanels displayed must change their string; in this code only one of them actually react!
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/**
*
* #author antonioruffolo
*/
public class TwoPanelsTest extends JFrame {
public TwoPanelsTest() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
setSize(800, 600);
PanelTest panelTest1= new PanelTest();
PanelTest panelTest2= new PanelTest();
GridBagLayout layout= new GridBagLayout();
this.setLayout(layout);
GridBagConstraints c = new GridBagConstraints();
c.ipadx = 220;
c.ipady = 390;
c.insets.right= 0;
c.insets.left=30;
layout.setConstraints(panelTest1, c);
this.add(panelTest1);
layout.setConstraints(panelTest2, c);
c.ipadx = 220;
c.ipady = 390;
c.insets.right=250;
c.insets.left=50;
this.add(panelTest2);
setVisible(true);
setLocationRelativeTo(null);
setTitle("Test");
setFocusable(false);
}
private class PanelTest extends JPanel{
private String string="I'm not called by the event";
private InputMap inputmap;
private ActionMap actionmap;
public PanelTest(){
setFocusable(false);
setDoubleBuffered(true);
this.setBackground(Color.WHITE);
inputmap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
actionmap = getActionMap();
actionmap.put("up", new ActionController(this));
}
public void setString(String string){
this.string=string;
}
#Override
public void paintComponent( Graphics g){
super.paintComponent(g);
Font infoFont= new Font("OCR A Std", Font.BOLD, 10);
g.setFont(infoFont);
g.drawString(string, 10, 50);
}
}//PanelTest
private class ActionController extends AbstractAction{
private PanelTest panel;
public ActionController (PanelTest panel){
this.panel=panel;
}
#Override
public void actionPerformed(ActionEvent ae) {
panel.setString("Action performed");
panel.repaint();
}
}
public static void main(String[] args) {
TwoPanelsTest t = new TwoPanelsTest();
}
}
Instead of KeyListener, use Key Bindings and have distinct Action implementations for each panel. By using the WHEN_ANCESTOR_OF_FOCUSED_COMPONENT input map, both panels can respond.
Addendum: Because the search ends after finding a valid binding for the key, the example below forwards the event to the elements of a List<MyPanel>, each of which can respond differently via an available Action.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/** #see http://stackoverflow.com/q/10011564/230513 */
public class TwoPanelsTest extends JFrame {
private MyPanel one = new MyPanel("One");
private MyPanel two = new MyPanel("Two");
private List<MyPanel> list = Arrays.asList(one, two);
public TwoPanelsTest() {
super("TwoPanelsTest");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel panel = new JPanel(new GridLayout(0, 1, 10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.add(one);
panel.add(two);
panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
panel.getActionMap().put("up", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
for (MyPanel panel : list) {
panel.getAction().actionPerformed(e);
}
}
});
this.add(panel);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
private static class MyPanel extends JPanel {
private String string = " will be updated though its action.";
private Action action = new UpdateAction(this);
private String name;
private JLabel label;
public MyPanel(String name) {
this.name = name;
this.label = new JLabel(name + string, JLabel.CENTER);
this.setLayout(new GridLayout());
this.setFocusable(true);
this.add(label);
}
public Action getAction() {
return action;
}
private void update() {
label.setText(name + ": " + System.nanoTime());
}
private static class UpdateAction extends AbstractAction {
private MyPanel panel;
public UpdateAction(MyPanel panel) {
this.panel = panel;
}
#Override
public void actionPerformed(ActionEvent ae) {
panel.update();
}
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
TwoPanelsTest t = new TwoPanelsTest();
}
});
}
}
One of the ways is use methods from SwingUtilities for Java6 (notice SwingUtilities for Java7 have got a few changes, but not important in this case) is possible to redirect, distribute, multiple events that came from Standard Swing Listeners, simple example about redirect mouse events from one container to the another,
You should create a XXListener implementation and add that listener by .addXXListener to all the components you need.
You can use the observer pattern for this.
http://en.wikipedia.org/wiki/Observer_pattern
MyKeyEventListener listener = new MyKeyEventListener();
JPanel one = new JPanel();
one.addKeyListener(listener);//method might be wrong
JPanel two = new JPanel();
two.addKeyListener(listener);
listener.addObserver(one);
listener.addObserver(two);
I asked about this before, but, I decided to start a new thread with a SCCE, it compiles sucessfully but it doesn't insert the image. Can anyone troubleshoot as to why it does this? Thanks, Chris.
Here is the full code:
package mathnotesplus;
import java.awt.Dimension;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.SwingUtilities;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.text.*;
/**
*
* #author ChrisCates
*/
public class MathNotesPlus extends JFrame implements TreeSelectionListener {
/**
* #param args the command line arguments
*/
public MathNotesPlus() {
setTitle("Math Notes Plus");
setSize(800, 600);
initUI();
}
JPanel panel;
JTextPane textpane;
JTree navigation;
StyledDocument document;
public void initUI(){
//The panel.
panel = new JPanel();
//Textpane and JTree
textpane = new JTextPane();
navigation = new JTree();
navigation.addTreeSelectionListener(this);
//Preferred Resolution Size
navigation.setPreferredSize(new Dimension(100, 600));
textpane.setPreferredSize(new Dimension(700, 600));
//Insertion of image into the document.
try {
document = (StyledDocument)textpane.getDocument();
Style style = document.addStyle("StyleName", null);
StyleConstants.setIcon(style, new ImageIcon("sigma.png"));
document.insertString(document.getLength(), "ignored text", style);
} catch (BadLocationException e){
System.err.println("ERROR");
}
//Putting everything into the program.
panel.add(navigation);
panel.add(textpane);
add(panel);
pack();
}
public static void main(String[] args) {
// TODO code application logic here
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MathNotesPlus app = new MathNotesPlus();
app.setVisible(true);
}
});
}
#Override
public void valueChanged(TreeSelectionEvent e) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
Base on this working example (a very close variant of your code) that works, I can only guess that your code is failing because the image is not being found.
import java.awt.Dimension;
import java.awt.Image;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
import javax.swing.text.*;
import java.net.URL;
import javax.imageio.ImageIO;
public class MathNotesPlus extends JFrame {
JPanel panel;
JTextPane textpane;
StyledDocument document;
public MathNotesPlus() {
setTitle("Math Notes Plus");
setSize(800, 600);
initUI();
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
public void initUI(){
//The panel.
panel = new JPanel();
//Textpane
textpane = new JTextPane();
textpane.setPreferredSize(new Dimension(700, 600));
//Insertion of image into the document.
try {
Image image = ImageIO.read(new URL(
"http://pscode.org/media/stromlo1.jpg"));
document = (StyledDocument)textpane.getDocument();
Style style = document.addStyle("StyleName", null);
StyleConstants.setIcon(style, new ImageIcon(image));
document.insertString(document.getLength(), "ignored text", style);
} catch (Exception e){
e.printStackTrace();
}
//Putting everything into the program.
panel.add(textpane);
add(panel);
pack();
}
public static void main(String[] args) {
// TODO code application logic here
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MathNotesPlus app = new MathNotesPlus();
app.setVisible(true);
}
});
}
}