Results 1 to 4 of 4
Like Tree1Likes
  • 1 Post By kjkrum

Thread: Clickable text in a component

  1. #1
    kjkrum's Avatar
    kjkrum is offline Senior Member
    Join Date
    Apr 2011
    Location
    Tucson, AZ
    Posts
    1,060
    Rep Power
    6

    Default Clickable text in a component

    I'm looking for a way to create a text box, like a JTextArea, that has links that underline when you hover over them. Ideally, when a link is clicked it would call a listener method with the link text as an argument. This is not for HTML. It's more like a remote log file browser.

    Edit: the component does not need to do any lexing or parsing. When text is appended to it, the caller will specify if it is a link or not.

    Does anyone know if the terminal component in the JTA telnet/ssh library can do this? Or know of another library that can?
    Last edited by kjkrum; 10-31-2011 at 06:40 PM.
    Get in the habit of using standard Java naming conventions!

  2. #2
    kjkrum's Avatar
    kjkrum is offline Senior Member
    Join Date
    Apr 2011
    Location
    Tucson, AZ
    Posts
    1,060
    Rep Power
    6

    Default Re: Clickable text in a component

    Bump, and here is what I have learned.

    I could use a JEditorPane and set it to content type text/html. The text I want to display is not HTML, it's ASCII with ANSI X3.64 escape codes. I'd have to parse and convert that to HTML, and insert anchor tags for the text I want to be links.

    I've also looked a bit at the internals of the JTA SwingTerminal and VDUBuffer classes. Without modifying that library at all, I think I could use mouse listeners on the SwingTerminal to convert pointer position to character coordinates and change the text attributes in the VDUBuffer when the mouse moves in or out of text I want to be a hyperlink. And also detect clicks the same way.

    Both of these possibilities would be a lot of work, though. I just want to make sure there's no off-the-shelf solution before I do this.
    Get in the habit of using standard Java naming conventions!

  3. #3
    kjkrum's Avatar
    kjkrum is offline Senior Member
    Join Date
    Apr 2011
    Location
    Tucson, AZ
    Posts
    1,060
    Rep Power
    6

    Default Re: Clickable text in a component

    This turned out to be a bit easier than I expected. I extended the SwingTerminal class from JTA and gave it some hyperlink capabilities. This two-file demo is just a crude proof of concept. JTA has some poorly chosen access modifiers, and I've wantonly abused them in this demo. I think for actual use, I'm going to create my own API and wrap a SwingTerminal and VDUBuffer inside it.

    HyperlinkTerminal.java

    Java Code:
    import java.awt.Cursor;
    import java.awt.Font;
    import java.awt.Point;
    import java.awt.event.MouseEvent;
    
    import de.mud.terminal.SwingTerminal;
    import de.mud.terminal.VDUBuffer;
    
    public class HyperlinkTerminal extends SwingTerminal {
    	private static final long serialVersionUID = 1L;
    	
    	
    	/**
    	 * Link character attribute.  First bit above the color attributes.
    	 */
    	public static final int LINK = 1 << 14;
    	
    	/**
    	 * Character coordinates of the last mouse press on button 1.
    	 */
    	private Point lastPress = null;
    	
    	/**
    	 * Character coordinates of the beginning of the link currently
    	 * highlighted by the mouse pointer.
    	 */
    	private Point hoverBegin = null;
    	
    	/**
    	 * Length of the link currently highlighted by the mouse pointer.
    	 */
    	private int hoverLength = 0;
    	
    	/**
    	 * Character buffer.  Hides private superclass field of the same name.
    	 */
    	private VDUBuffer buffer;
    	
    	/**
    	 * Normal cursor.
    	 */
    	Cursor normalCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
    	
    	/**
    	 * Hand cursor.
    	 */
    	Cursor handCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
    	
    	
    	@Override
    	public void mousePressed(MouseEvent evt) {
    		super.mousePressed(evt);
    		if(evt.getButton() == MouseEvent.BUTTON1) {
    			lastPress = mouseGetPos(evt.getPoint());
    		}
    	}
    
    	
    	@Override
    	public void mouseReleased(MouseEvent evt) {
    		super.mouseReleased(evt);
    		if(evt.getButton() == MouseEvent.BUTTON1) {
    			Point release = mouseGetPos(evt.getPoint());
    			if(release.equals(lastPress)) {
    				int attr = buffer.charAttributes[release.y + buffer.windowBase][release.x];
    				if(attr >= LINK) {
    					// TODO: it's a link
    				}
    				
    				// debug
    				System.err.println("click: " + release.y + ", " + release.x);
    				System.err.println("attributes at click: " + attr);
    			}
    		}
    	}
    
    	
    	@Override
    	public void mouseMoved(MouseEvent evt) {
    		super.mouseMoved(evt);
    		Point p = mouseGetPos(evt.getPoint());
    		
    		// if a hover is currently on...
    		if(hoverBegin != null) {
    			// find out if we are still in it
    			if(p.y == hoverBegin.y && p.x >= hoverBegin.x && p.x < hoverBegin.x + hoverLength) {
    				return;
    			}
    			// no... so turn it off
    			else {
    				System.err.println("exited link");
    				toggleHover();
    				hoverBegin = null;
    				setCursor(normalCursor);
    			}
    		}
    		
    		// find out if we moved into a new link
    		int attr = buffer.charAttributes[p.y + buffer.windowBase][p.x];
    		if(attr >= LINK) {
    			System.err.println("entered link");
    			// find the first column in the link
    			int first = p.x;
    			while(first > 0 && buffer.charAttributes[p.y + buffer.windowBase][first - 1] >= LINK) {
    				--first;
    			}
    			// find the last column
    			int last = p.x;
    			while(last < buffer.width - 1 && buffer.charAttributes[p.y + buffer.windowBase][last + 1] >= LINK) {
    				++last;
    			}
    			
    			hoverBegin = new Point(first, p.y);
    			hoverLength = last - first + 1;
    			
    			System.err.println("begin: " + hoverBegin.toString() + " length: " + hoverLength);
    			toggleHover();
    			setCursor(handCursor);			
    		}
    		// debug
    		//System.err.println("motion: " + p.y + ", " + p.x);
    	}
    	
    	
    	/**
    	 * Toggles the current hover highlight.
    	 */
    	void toggleHover() {
    		for(int i = 0; i < hoverLength; ++i) {
    			int attr = buffer.charAttributes[hoverBegin.y + buffer.windowBase][hoverBegin.x + i];
    			attr = attr ^ VDUBuffer.UNDERLINE;
    			buffer.charAttributes[hoverBegin.y + buffer.windowBase][hoverBegin.x + i] = attr;
    		}
    		buffer.markLine(hoverBegin.y, 1);
    		redraw();
    	}
    	
    	
    	public void makeLink(int row, int column, int length) {
    		// TODO: sanity check
    		for(int i = 0; i < length; ++i) {
    			int attr = buffer.charAttributes[row + buffer.windowBase][column + i];
    			attr = attr | LINK;
    			buffer.charAttributes[row + buffer.windowBase][column + i] = attr;
    		}
    	}
    	
    
    	public HyperlinkTerminal(VDUBuffer buffer, Font font) {
    		super(buffer, font);
    		this.buffer = buffer;
    	}
    	
    	
    	public HyperlinkTerminal(VDUBuffer buffer) {
    		super(buffer);
    		this.buffer = buffer;
    	}
    
    }
    Demo.java

    Java Code:
    import javax.swing.JFrame;
    
    import de.mud.terminal.SwingTerminal;
    import de.mud.terminal.vt320;
    
    public class Demo {
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		// stock components
    		vt320 buffer = new vt320(80, 25) {
    			// dummy method
    			public void write(byte[] b) { }
    		};
    		HyperlinkTerminal terminal = new HyperlinkTerminal(buffer);
    		terminal.setResizeStrategy(SwingTerminal.RESIZE_SCREEN);
    
    		// frame to display them
    		JFrame frame = new JFrame("Hyperlink SwingTerminal Demo");
    		frame.getContentPane().add(terminal);
    		
    		// finalize frame
    		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		frame.pack();
    		frame.setVisible(true);
    		
    		// print some text in the terminal
    		buffer.putString("\r\nClick the number: 12345\r\n");
    		terminal.makeLink(1, 18, 5);
    		
    		//buffer.setBufferSize(30);
    		//for(int i = 0; i < 50; ++i) {
    		//	buffer.putString("line " + (i + 1) + "\r\n");
    		//}
    		//buffer.putString("\033[1;31mHAAAAAY! \033[0;4m0123456789\033[0m\r\nABCDEFGHIJKLMNOPQRSTUVWXYZ ");
    		//buffer.putString("window base: " + buffer.getWindowBase());
    	}
    }
    Fubarable likes this.
    Get in the habit of using standard Java naming conventions!

  4. #4
    camickr is offline Senior Member
    Join Date
    Jul 2009
    Posts
    1,236
    Rep Power
    7

    Default Re: Clickable text in a component

    1) Create an ArrayList that tracks all the links. I guess you would need to keep track of all offset and the link itself.
    2) Add a MouseListener to the text area to listen for mouse moves and mouse clicks
    3) Get the mouse point from the MouseEvent so you can use the viewToModel(...) method of the text area to get the offset of the character at the mouse
    4) Loop through the ArrayList to determine is the mouse if over a link
    5) When over a link add a Highlighter to the text area. If not over a link, then remove all Highlighters.

    Same basic logic for a click.

    You will need to create a custom Highlighter to display a line under the text. You can start with the Rectangle Painter Java Tips Weblog.

Similar Threads

  1. JButton not clickable
    By StevenTNorris in forum AWT / Swing
    Replies: 5
    Last Post: 11-09-2012, 08:59 PM
  2. Make a clickable moving image?
    By scheffetz in forum New To Java
    Replies: 1
    Last Post: 04-08-2011, 07:54 PM
  3. Disable copy paste in SWT Text Component
    By subysri in forum SWT / JFace
    Replies: 1
    Last Post: 09-18-2010, 10:11 AM
  4. Double Clickable Application
    By ellias2007 in forum Advanced Java
    Replies: 7
    Last Post: 03-08-2010, 04:08 PM
  5. Putting clickable icons on an image
    By szakee in forum AWT / Swing
    Replies: 3
    Last Post: 04-06-2009, 10:25 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •