Created
August 25, 2015 11:35
-
-
Save raydac/1af1a658af6a442f77bd to your computer and use it in GitHub Desktop.
an adaptation of javax.swing.JLabel to process clicks on links in HTML content
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2015 Igor Maznitsa. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.igormaznitsa.ui; | |
import java.awt.Cursor; | |
import java.awt.Point; | |
import java.awt.event.MouseAdapter; | |
import java.awt.event.MouseEvent; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Locale; | |
import java.util.Properties; | |
import java.util.concurrent.CopyOnWriteArrayList; | |
import javax.swing.Icon; | |
import javax.swing.JLabel; | |
import javax.swing.text.SimpleAttributeSet; | |
import javax.swing.text.View; | |
import javax.swing.text.html.HTML; | |
import javax.swing.text.html.HTMLDocument; | |
/** | |
* Small adaptation of javax.swing.JLabel to catch clicks on HTML links in the | |
* label. | |
* <b>The Component supports only HTML text and if the text is not into <html> | |
* tags then such tags will be added automatically.</b> | |
* | |
* @author Igor Maznitsa (http://www.igormaznitsa.com) | |
* | |
* @version 1.00 | |
*/ | |
public class JHtmlLabel extends JLabel { | |
private static final long serialVersionUID = -166975925687523220L; | |
/** | |
* Listener to get notification about activation of a link. | |
*/ | |
public interface LinkListener { | |
/** | |
* Called if detected activation of a link placed on the label. | |
* @param source the label, must not be null | |
* @param link the link to be processed, must not be null | |
*/ | |
void onLinkActivated(JHtmlLabel source, String link); | |
} | |
/** | |
* Internal auxiliary class to keep cached parameters of found link elements. | |
*/ | |
private static final class HtmlLinkAddress { | |
private final String address; | |
private final int start; | |
private final int end; | |
HtmlLinkAddress(final String address, final int startOffset, final int endOffset) { | |
this.address = address; | |
this.start = startOffset; | |
this.end = endOffset; | |
} | |
String getHREF() { | |
return this.address; | |
} | |
boolean checkPosition(final int position) { | |
return position >= this.start && position < this.end; | |
} | |
} | |
/** | |
* Inside cache of detected link elements. | |
*/ | |
private List<HtmlLinkAddress> linkCache = null; | |
private final List<LinkListener> linkListeners = new CopyOnWriteArrayList<LinkListener>(); | |
private boolean showLinkAddressInToolTip = false; | |
private int minClickCountToActivateLink = 1; | |
public JHtmlLabel(final String text, final Icon icon, final int horizontalAlignment) { | |
super(text, icon, horizontalAlignment); | |
final JHtmlLabel theInstance = this; | |
final MouseAdapter mouseAdapter = new MouseAdapter() { | |
@Override | |
public void mouseMoved(final MouseEvent e) { | |
final String link = getLinkAtPosition(e.getPoint()); | |
if (link == null) { | |
if (showLinkAddressInToolTip) { | |
setToolTipText(null); | |
} | |
setCursor(Cursor.getDefaultCursor()); | |
} | |
else { | |
if (showLinkAddressInToolTip) { | |
setToolTipText(link); | |
} | |
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); | |
} | |
} | |
@Override | |
public void mouseClicked(final MouseEvent e) { | |
if (e.getClickCount() >= minClickCountToActivateLink) { | |
final String link = getLinkAtPosition(e.getPoint()); | |
if (link != null) { | |
for (final LinkListener l : linkListeners) { | |
l.onLinkActivated(theInstance, link); | |
} | |
} | |
} | |
} | |
}; | |
this.addMouseListener(mouseAdapter); | |
this.addMouseMotionListener(mouseAdapter); | |
} | |
public JHtmlLabel(final String text, final int horizontalAlignment) { | |
this(text, null, horizontalAlignment); | |
} | |
public JHtmlLabel(final String text) { | |
this(text, null, LEADING); | |
} | |
public JHtmlLabel(final Icon image, final int horizontalAlignment) { | |
this(null, image, horizontalAlignment); | |
} | |
public JHtmlLabel(final Icon image) { | |
this(null, image, CENTER); | |
} | |
public JHtmlLabel() { | |
this("", null, LEADING); | |
} | |
public int getMinClickCountToActivateLink(){ | |
return this.minClickCountToActivateLink; | |
} | |
public void setMinClickCountToActivateLink(final int clickNumber){ | |
this.minClickCountToActivateLink = Math.max(1, clickNumber); | |
} | |
public boolean isShowLinkAddressInTooltip() { | |
return this.showLinkAddressInToolTip; | |
} | |
public void setShowLinkAddressInTooltip(final boolean flag) { | |
if (this.showLinkAddressInToolTip) { | |
if (!flag) { | |
this.setToolTipText(null); | |
} | |
} | |
this.showLinkAddressInToolTip = flag; | |
} | |
public void addLinkListener(final LinkListener l) { | |
this.linkListeners.add(l); | |
} | |
public void removeLinkListener(final LinkListener l) { | |
this.linkListeners.remove(l); | |
} | |
public void replaceMacroses(final Properties properties) { | |
String text = this.getText(); | |
for (final String k : properties.stringPropertyNames()) { | |
text = text.replace("${" + k + "}", properties.getProperty(k)); | |
} | |
this.setText(text); | |
} | |
@Override | |
public void setText(final String text) { | |
super.setText(text.toLowerCase(Locale.ENGLISH).trim().startsWith("<html>") ? text : "<html>" + text + "</html>"); | |
this.linkCache = null; | |
} | |
private void cacheLinkElements() { | |
this.linkCache = new ArrayList<HtmlLinkAddress>(); | |
final View view = (View) this.getClientProperty("html"); | |
if (view != null) { | |
final HTMLDocument doc = (HTMLDocument) view.getDocument(); | |
final HTMLDocument.Iterator it = doc.getIterator(HTML.Tag.A); | |
while (it.isValid()) { | |
final SimpleAttributeSet s = (SimpleAttributeSet) it.getAttributes(); | |
final String link = (String) s.getAttribute(HTML.Attribute.HREF); | |
if (link != null) { | |
this.linkCache.add(new HtmlLinkAddress(link, it.getStartOffset(), it.getEndOffset())); | |
} | |
it.next(); | |
} | |
} | |
} | |
private String getLinkAtPosition(final Point point) { | |
if (this.linkCache == null) { | |
cacheLinkElements(); | |
} | |
final AccessibleJLabel accessibleJLabel = (AccessibleJLabel) this.getAccessibleContext().getAccessibleComponent(); | |
final int textIndex = accessibleJLabel.getIndexAtPoint(point); | |
for (final HtmlLinkAddress l : this.linkCache) { | |
if (l.checkPosition(textIndex)) { | |
return l.getHREF(); | |
} | |
} | |
return null; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment