Problems with a JTextField inside of a JTextPane

I have a JTextPane that needs to have individual words or phrases (in the DefaultStyledDocument) that can generate a MouseEvent. I've tried wrapping a JTextField in a Style (when adding it to the StyledDocument), this works nice and does create the type of MouseEvents that I'm looking for. But there a few problems...

1. The line height increases when inserting a JTextField. Screenshot: http://mirc-dll.com/images/java.gif

2. When highlighting in the document, the JTextField doesn't highlight.

3. When a JTextField is on a line of its own, the JTextField mouse events extend for the entire line, I only want to see the mouse events when the mouse is over the actual text.

Here is an example of both problems...

Note: for some reason, when I put a newline at the end of the string in doc.insertString it looks fine, but only in this example... In my actual code is still doesn't look right, It could be because I'm placing newlines at the beginning of strings (in the actual code), to avoid an empty line on the bottom of the JTextPane

Note: You can highlight just the hotlink, but when highlighting all text, it is not highlighted. Also for some reason, the example doesn't copy to the clipboard properly, unlike my actual code... ?

Note: Using JDK 6 Update 1

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.text.*;

publicclass JTextPanelextends JPanel

{

finalstaticprivate StringFONTNAME ="SansSerif";

finalstaticprivateintFONTSIZE = 14;

private JScrollPanescrollPane;

private JTextPanetextPane;

private Stylenormal;

private Stylehotlink;

publicstaticvoid main(String [] args)

{

try{

SwingUtilities.invokeAndWait(new Runnable()

{

publicvoid run()

{

JFrameframe =new JFrame("Example");

Container pane = frame.getContentPane();

JTextPanel panel =new JTextPanel();

panel.setPreferredSize(new Dimension(180, 180));

panel.setMinimumSize(new Dimension(50, 50));

pane.add( panel );

frame.setSize( 190, 210 );

frame.setLocation( 0, 0 );

frame.setLayout(new FlowLayout() );

frame.setAlwaysOnTop(true);

frame.setVisible(true);

}

});

}catch (Exception e){}

}

public JTextPanel()

{

scrollPane =new JScrollPane(new JTextPane() );

scrollPane.setVerticalScrollBarPolicy(scrollPane.VERTICAL_SCROLLBAR_ALWAYS);

scrollPane.setPreferredSize(new Dimension(160, 160));

scrollPane.setMinimumSize(new Dimension(50, 50));

textPane = (JTextPane)scrollPane.getViewport().getView();

textPane.setEditable(false);

add(scrollPane);

print();

}

privatevoid print()

{

Style startStyle =

StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);

normal = textPane.addStyle(null, startStyle);

StyleConstants.setFontFamily(normal, FONTNAME);

StyleConstants.setFontSize(normal, FONTSIZE);

JTextField textField =new JTextField("hotlink");

DefaultStyledDocumentdoc =

(DefaultStyledDocument)textPane.getStyledDocument();

textField.setFont(new Font(FONTNAME, 0, FONTSIZE) );

textField.setBorder(null);

textField.setOpaque(false);

textField.setEditable(false);

textField.addMouseListener(new MouseListener()

{

publicvoid mouseEntered(MouseEvent e)

{

System.out.println("Over Hotlink");

}

publicvoid mouseExited(MouseEvent e)

{

System.out.println("Left Hotlink");

}

publicvoid mousePressed(MouseEvent e)

{

System.out.println("Pressed Hotlink");

}

publicvoid mouseClicked(MouseEvent e){}

publicvoid mouseReleased(MouseEvent e){}

});

hotlink = doc.addStyle(null, normal);

StyleConstants.setComponent(hotlink, textField);

StyleConstants.setBold(hotlink,false);

StyleConstants.setUnderline(hotlink,true);

try{

doc.insertString(doc.getLength(),"Normal ", normal);

doc.insertString(doc.getLength()," ", hotlink);

doc.insertString(doc.getLength(),"Normal\n", normal);

}catch (Exception e){ e.printStackTrace();}

}

}

Any ideas, or other ways of doing this?

Naquada

[7500 byte] By [Naquadaa] at [2007-11-27 11:48:53]
# 1

Use a JEditorPane, it supports HyperLinks.

camickra at 2007-7-29 18:21:55 > top of Java-index,Desktop,Core GUI APIs...
# 2

Thanks for trying, although I already knew about this one. I decided not to use becuase then I'm forced into using an HTMLDocument, and I am not working with HTML documents (this is for a chat client). All I want is the mouse events, nothing more. :)

Naquadaa at 2007-7-29 18:21:55 > top of Java-index,Desktop,Core GUI APIs...
# 3

Then just set the attributes of the "link" text so that is looks like a hyperlink. Then you add a mouse listener to the text pane. When the user clicks on the text you get the attributes of the clicked text to determine if they contain you "link" attributes.

camickra at 2007-7-29 18:21:55 > top of Java-index,Desktop,Core GUI APIs...
# 4

I like where you are going with this suggestion, but how can I tell what document position my mouse is hovering over (still need exit/enter to change cursor)?

Naquadaa at 2007-7-29 18:21:55 > top of Java-index,Desktop,Core GUI APIs...
# 5

You can use a MouseListener to handle the mouseMoved event. Then you get the mouse x, y coordinates and you can use the text pane viewToModel(...) method to calculate the offset of the text in the model. Once you know the offset of the text you can get the attributes. Read up on the JTextPane and Document APIs.

camickra at 2007-7-29 18:21:55 > top of Java-index,Desktop,Core GUI APIs...
# 6

You rule, thank you so much!!! :)

If anyone else is interested in source that can do this...

// This is still missing a cursor change for a hotlink, and

// methods like hotlinkEnter, hotlinkExit, and hotlinkSelected...

// This is a good enough demo of the procedure though.

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.text.*;

public class JTextPanel extends JPanel

{

final static private StringATTRIB_HOTLINK = "HotLinkAttribute";

final static private StringFONTNAME = "SansSerif";

final static private intFONTSIZE = 14;

private JScrollPane scrollPane;

private JTextPanetextPane;

private Stylenormal;

private Stylehotlink;

private DefaultStyledDocumentdoc;

public static void main(String [] args)

{

try {

SwingUtilities.invokeAndWait(new Runnable()

{

public void run()

{

JFrameframe = new JFrame("Example");

Container pane = frame.getContentPane();

JTextPanel panel = new JTextPanel();

panel.setPreferredSize(new Dimension(180, 180));

panel.setMinimumSize(new Dimension(50, 50));

pane.add( panel );

frame.setSize( 190, 210 );

frame.setLocation( 0, 0 );

frame.setLayout( new FlowLayout() );

frame.setAlwaysOnTop(true);

frame.setVisible(true);

}

});

} catch (Exception e) { e.printStackTrace(); }

}

public JTextPanel()

{

scrollPane = new JScrollPane( new JTextPane() );

scrollPane.setVerticalScrollBarPolicy(scrollPane.VERTICAL_SCROLLBAR_ALWAYS);

scrollPane.setPreferredSize(new Dimension(160, 160));

scrollPane.setMinimumSize(new Dimension(50, 50));

textPane = (JTextPane)scrollPane.getViewport().getView();

textPane.setEditable(false);

textPane.addMouseMotionListener(new MouseMotionListener()

{

public void mouseDragged(MouseEvent e) { }

public void mouseMoved(MouseEvent e)

{

Point pt = new Point(e.getX(), e.getY());

intposition = textPane.viewToModel(pt);

DefaultStyledDocument doc = (DefaultStyledDocument)

textPane.getStyledDocument();

Elementelement = doc.getCharacterElement(position);

AttributeSetattrib = element.getAttributes();

booleanisHot = (attrib.getAttribute(ATTRIB_HOTLINK) != null);

if (isHot)

{

try {

String hotlink = doc.getText(element.getStartOffset(),

(element.getEndOffset() - element.getStartOffset()));

System.out.println("Hotlink: @(" + e.getX() + ", " +

e.getY() + ") " + hotlink);

} catch (Exception except) { }

}

}

});

doc = (DefaultStyledDocument)textPane.getStyledDocument();

add(scrollPane);

setStyles();

println("normal");

printlnHotlink("Hotlink #1");

println("normal");

print("**** ");

printHotlink("Hotlink #2");

println(" ****");

}

private void setStyles()

{

Style startStyle =

StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);

normal = textPane.addStyle(null, startStyle);

StyleConstants.setFontFamily(normal, FONTNAME);

StyleConstants.setFontSize(normal, FONTSIZE);

hotlink = textPane.addStyle(null, startStyle);

hotlink.addAttribute(ATTRIB_HOTLINK, "hotlink");

StyleConstants.setFontFamily(hotlink, FONTNAME);

StyleConstants.setFontSize(hotlink, FONTSIZE);

StyleConstants.setUnderline(hotlink, true);

}

public void print(String text)

{

try {

doc.insertString(doc.getLength(), text, normal);

} catch (Exception e) { e.printStackTrace(); }

}

public void println(String text)

{

try {

doc.insertString(doc.getLength(), text + "\n", normal);

} catch (Exception e) { e.printStackTrace(); }

}

public void printHotlink(String text)

{

try {

doc.insertString(doc.getLength(), text, hotlink);

} catch (Exception e) { e.printStackTrace(); }

}

public void printlnHotlink(String text)

{

try {

doc.insertString(doc.getLength(), text, hotlink);

// Don't want the \n considered part of the hotlink

doc.insertString(doc.getLength(), "\n", normal);

} catch (Exception e) { e.printStackTrace(); }

}

}

Naquadaa at 2007-7-29 18:21:55 > top of Java-index,Desktop,Core GUI APIs...
# 7

> This is still missing a cursor change for a hotlink

Not sure why you would need a hotlinkEnter or Exit. Just change your if statement to include the following.

if (isHot)

textPane.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));

else

textPane.setCursor(null);

What you really need is a MouseListener to handle mouseClicked. That is when you would get the actual hotlink from the text pane and invoke your hotlink processing.

camickra at 2007-7-29 18:21:55 > top of Java-index,Desktop,Core GUI APIs...
# 8

Yeah realize it's missing the MouseListener; that will be easy to add though. Good point about not needing exit/enter anytime after changing the cursor.

Message was edited by:

Naquada

Naquadaa at 2007-7-29 18:21:55 > top of Java-index,Desktop,Core GUI APIs...