package com.vistatec.ocelot.freme.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.FontMetrics;
import java.awt.Point;
import java.awt.SystemColor;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
import com.vistatec.ocelot.segment.model.BaseSegmentVariant;
import com.vistatec.ocelot.segment.model.enrichment.Enrichment;
/**
* Frame displaying enriched words highlighted in the text.
*/
public class EnrichmentFrame extends JDialog implements Runnable,
ActionListener {
/** The serial version UID. */
private static final long serialVersionUID = -1930402687244966333L;
/** The close button. */
private JButton btnClose;
/** The list of labels. */
private List<JLabel> labels;
/** The location where the frame is displayed. */
private Point position;
/**
* Constructor.
*
* @param fragment
* the Ocelot fragment.
* @param owner
* the owner window.
* @param position
* the position.
*/
public EnrichmentFrame(final BaseSegmentVariant fragment,
final Window owner, final Point position) {
super(owner);
buildLabels(fragment);
this.position = position;
}
/**
* Constructor.
*
* @param fragment
* the Ocelot fragment.
* @param owner
* the owner window.
*/
public EnrichmentFrame(final BaseSegmentVariant fragment, final Window owner) {
this(fragment, owner, null);
}
private Component makeMainPanel() {
JTextPane textPane = new JTextPane();
JScrollPane scrollPane = new JScrollPane(textPane);
scrollPane.setPreferredSize(new Dimension(400, 200));
textPane.setEditable(false);
StyledDocument styleDoc = textPane.getStyledDocument();
Style def = StyleContext.getDefaultStyleContext().getStyle(
StyleContext.DEFAULT_STYLE);
Style regular = styleDoc.addStyle("regular", def);
Style text = styleDoc.addStyle("regular", def);
JLabel sampleLabel = new JLabel();
StyleConstants.setFontSize(text, sampleLabel.getFont().getSize());
StyleConstants.setFontFamily(text, sampleLabel.getFont().getFamily());
StyleConstants.setBold(text, true);
Style labelStyle = styleDoc.addStyle("label", regular);
try {
for (JLabel label : labels) {
if (label instanceof EnrichedLabel) {
StyleConstants.setComponent(labelStyle, label);
styleDoc.insertString(styleDoc.getLength(), " ", labelStyle);
} else {
styleDoc.insertString(styleDoc.getLength(),
label.getText(), text);
}
}
} catch (BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return scrollPane;
}
/**
* Makes the bottom panel.
*
* @return the bottom panel
*/
private Component makeBottomPanel() {
btnClose = new JButton("Close");
btnClose.addActionListener(this);
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.RIGHT));
panel.add(btnClose);
return panel;
}
/**
* Builds the labels from the text and the enrichments retrieved by the
* Ocelot fragment.
*
* @param fragment
* the Ocelot fragments.
*/
private void buildLabels(BaseSegmentVariant fragment) {
labels = new ArrayList<JLabel>();
if (fragment.getEnirchments() != null) {
List<Enrichment> fragEnrichments = new ArrayList<>(fragment.getEnirchments());
Collections.sort(fragEnrichments, new EnrichmentComparator());
JLabel label = null;
String fragDisplayText = fragment.getDisplayText();
int startIndex = 0;
EnrichedLabel lastEnrichedLabel = null;
for (Enrichment e : fragEnrichments) {
if (lastEnrichedLabel != null
&& lastEnrichedLabel.containsOffset(e
.getOffsetStartIdx())) {
lastEnrichedLabel.addEnrichment(e);
if (startIndex < e.getOffsetEndIdx()) {
startIndex = e.getOffsetEndIdx();
}
} else {
label = new JLabel(fragDisplayText.substring(startIndex,
e.getOffsetStartIdx()));
labels.add(label);
lastEnrichedLabel = new EnrichedLabel(
fragDisplayText.substring(e.getOffsetStartIdx(),
e.getOffsetEndIdx()));
lastEnrichedLabel.addEnrichment(e);
labels.add(lastEnrichedLabel);
startIndex = e.getOffsetEndIdx();
}
}
if (startIndex < fragDisplayText.length()) {
labels.add(new JLabel(fragDisplayText.substring(startIndex)));
}
}
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
setTitle("Enrichment");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
add(makeMainPanel());
add(makeBottomPanel(), BorderLayout.SOUTH);
pack();
if (position != null) {
setLocation(position);
} else {
setLocationRelativeTo(getOwner());
}
setVisible(true);
}
/*
* (non-Javadoc)
*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(ActionEvent e) {
close();
}
/**
* Closes the frame.
*/
private void close() {
setVisible(false);
dispose();
}
}
/**
* Comparator for enrichments.
*/
class EnrichmentComparator implements Comparator<Enrichment> {
@Override
public int compare(Enrichment o1, Enrichment o2) {
int comparison = 0;
if (o1.getOffsetStartIdx() < o2.getOffsetStartIdx()) {
comparison = -1;
} else if (o1.getOffsetStartIdx() > o2.getOffsetStartIdx()) {
comparison = 1;
}
return comparison;
}
}
/**
* Label containing an enriched text.
*/
class EnrichedLabel extends JLabel implements ActionListener, MouseListener {
/** The serial version UID. */
private static final long serialVersionUID = -8815906147011864508L;
/** The plain text assigned to this label. */
private String plainText;
/** The enrichments assigned to this label. */
private List<Enrichment> enrichments;
/** The start index for this label within the whole fragment text. */
private int startIndex;
/** The end index for this label within the whole fragment text. */
private int endIndex;
/**
* States if this label is disabled: an enriched label is disabled if and
* only if all the assigned enrichments are disabled.
*/
private boolean disabled;
/** The balloon tip displayed for this label. */
private BalloonTip tip;
/**
* Constructor.
*
* @param text
* the text for this label.
*/
public EnrichedLabel(final String text) {
this.plainText = text;
startIndex = -1;
endIndex = -1;
initialize();
}
/**
* Initializes the label.
*/
private void initialize() {
String enrichedText = "<html><font color=\"blue\"><u><b>" + plainText
+ "</b></u></font></html>";
FontMetrics metrics = getFontMetrics(getFont());
final int width = metrics.charsWidth(plainText.toCharArray(), 0,
plainText.length());
setMaximumSize(new Dimension(width, getHeight()));
setMinimumSize(new Dimension(width, getHeight()));
setAlignmentY(0.85f);
setText(enrichedText);
setCursor(new Cursor(Cursor.HAND_CURSOR));
addMouseListener(this);
}
/**
* Adds an enrichment to this label.
*
* @param enrichment
* the enrichment.
*/
public void addEnrichment(final Enrichment enrichment) {
if (enrichments == null) {
enrichments = new ArrayList<Enrichment>();
}
enrichments.add(enrichment);
if (startIndex == -1 || startIndex > enrichment.getOffsetStartIdx()) {
startIndex = enrichment.getOffsetStartIdx();
}
if (endIndex == -1 || endIndex < enrichment.getOffsetEndIdx()) {
endIndex = enrichment.getOffsetEndIdx();
}
}
/*
* (non-Javadoc)
*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(ActionEvent e) {
if (!disabled) {
disabled = true;
String enrichedText = "<html><font color=\"gray\"><u>" + plainText
+ "</u></font></html>";
setText(enrichedText);
} else {
disabled = false;
String enrichedText = "<html><font color=\"blue\"><u><b>"
+ plainText + "</b></u></font></html>";
setText(enrichedText);
}
}
/**
* Checks if this label is disabled.
*
* @return <code>true</code> if the label is disabled; <code>false</code>
* otherwise
*/
public boolean isDisabled() {
return disabled;
}
/**
* Checks if this label contains a specific offset index.
*
* @param offestStartIdx
* the offset index.
* @return <code>true</code> if it contains this index; <code>false</code>
* otherwise
*/
public boolean containsOffset(final int offestStartIdx) {
return offestStartIdx >= startIndex && offestStartIdx <= endIndex;
}
/**
* Handles the event the mouse has been clicked over this label: the balloon
* tool tip gets opened.
*
* @param e
* the mouse event
*/
private void handleMouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
final Window ancestor = SwingUtilities.getWindowAncestor(this);
tip = new BalloonTip(SwingUtilities.getWindowAncestor(ancestor),
getDescriptionComponent(), getLocationOnScreen().x
+ getWidth() / 2, getLocationOnScreen().y
+ getHeight());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
tip.makeUI();
updateLabelStatus();
}
});
}
}
/**
* Gets the description component to be displayed within the balloon tool
* tip.
*
* @return the description component to be displayed within the balloon tool
* tip.
*/
public Component getDescriptionComponent() {
return new EnrichmentDetailsPanel(enrichments, SystemColor.info);
}
/*
* (non-Javadoc)
*
* @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
*/
@Override
public void mouseClicked(MouseEvent e) {
handleMouseClicked(e);
}
/*
* (non-Javadoc)
*
* @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
*/
@Override
public void mousePressed(MouseEvent e) {
handleMouseClicked(e);
}
/*
* (non-Javadoc)
*
* @see
* java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
*/
@Override
public void mouseReleased(MouseEvent e) {
// does nothing.
}
/*
* (non-Javadoc)
*
* @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
*/
@Override
public void mouseEntered(MouseEvent e) {
// does nothing
}
/**
* Updates the status of the label depending on the status of the embedded
* enrichments.
*/
private void updateLabelStatus() {
boolean newValue = true;
for (Enrichment e : enrichments) {
if (!e.isDisabled()) {
newValue = false;
}
}
if (newValue != disabled) {
disabled = newValue;
if (disabled) {
String enrichedText = "<html><font color=\"gray\"><u>"
+ plainText + "</u></font></html>";
setText(enrichedText);
} else {
String enrichedText = "<html><font color=\"blue\"><u><b>"
+ plainText + "</b></u></font></html>";
setText(enrichedText);
}
}
}
/*
* (non-Javadoc)
* @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
*/
@Override
public void mouseExited(MouseEvent e) {
//does nothing
}
}