package org.yamcs.ui.archivebrowser;
import org.yamcs.protobuf.Yamcs.ArchiveTag;
import org.yamcs.utils.TimeEncoding;
import javax.swing.*;
import javax.swing.event.MouseInputListener;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.font.LineMetrics;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.List;
public class TagTimeline extends JPanel implements MouseInputListener {
private static final long serialVersionUID = 1L;
private final TagBox tagBox;
List<ArchiveTag> tags;
ZoomSpec zoom;
int leftDelta;
BufferedImage image=null;
int row;
Font f;
TagTimeline(TagBox tagBox, List<ArchiveTag> tags, ZoomSpec zoom, int row, int leftDelta) {
super();
this.tagBox = tagBox;
this.zoom=zoom;
this.tags = tags;
this.row=row;
this.leftDelta=leftDelta;
JLabel l=new JLabel("X");
f = deriveFont(l.getFont());
l.setFont(f);
setMinimumSize(new Dimension(0,2+l.getPreferredSize().height));
setPreferredSize(getMinimumSize());
setMaximumSize(new Dimension(Integer.MAX_VALUE, 2+l.getPreferredSize().height));
addMouseMotionListener(this);
addMouseListener(this);
setOpaque(false);
}
private static Font deriveFont(Font f) {
return f.deriveFont(Font.PLAIN, f.getSize2D()-2);
}
@Override
public Point getToolTipLocation(MouseEvent e) {
return tagBox.getToolTipLocation(e);
}
private MouseEvent translateEvent(MouseEvent e, Component dest) {
return SwingUtilities.convertMouseEvent(e.getComponent(), e, dest);
}
@Override
public void mousePressed(MouseEvent e) {
long t=zoom.convertPixelToInstant(e.getX());
int index=time2Tag(tags,t);
tagBox.doMousePressed(translateEvent(e, tagBox), row, index);
}
@Override
public void mouseReleased(MouseEvent e) {
getParent().dispatchEvent(translateEvent(e, getParent()));
}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
@Override
public void mouseClicked(MouseEvent e) {}
ArchiveTag lastMouseTag=null;
@Override
public void mouseMoved(MouseEvent e) {
long t=zoom.convertPixelToInstant(e.getX()+leftDelta);
int index=time2Tag(tags, t);
ArchiveTag at=null;
if(index!=-1) at=tags.get(index);
if(at==lastMouseTag)return;
lastMouseTag=at;
if(at!=null) {
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
StringBuilder sb=new StringBuilder();
sb.append("<html>").append(at.getName()).append("<hr>");
if(at.hasStart()) {
sb.append("Start: ").append(TimeEncoding.toString(at.getStart())).append("<br>");
}
if(at.hasStop()) {
sb.append("Stop: ").append(TimeEncoding.toString(at.getStop())).append("<br>");
}
if(at.hasDescription()) {
sb.append(at.getDescription());
}
sb.append("</html>");
setToolTipText(sb.toString());
} else {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
setToolTipText(null);
}
}
static int time2Tag(List<ArchiveTag> tagList, long t) {
int min=0;
int max=tagList.size()-1;
int mid;
while(min<=max) {
mid=(min+max)>>1;
ArchiveTag atmid=tagList.get(mid);
if(!atmid.hasStart() || atmid.getStart()<=t) {
if(!atmid.hasStop() || atmid.getStop()>=t) {
return mid;
} else {
min=mid+1;
}
} else {
max=mid-1;
}
}
return -1;
}
@Override
public void mouseDragged(MouseEvent e) {
// TTM does not show the tooltip in mouseDragged() so we send a MOUSE_MOVED event
dispatchEvent(new MouseEvent(e.getComponent(), MouseEvent.MOUSE_MOVED, e.getWhen(), e.getModifiers(),
e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger(), e.getButton()));
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if(image==null) {
image=new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D big=image.createGraphics();
big.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
big.fillRect(0,0,getWidth(),getHeight());
big.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
for(ArchiveTag at:tags) {
Color bgcolor,fgcolor;
if(at.hasColor()){
bgcolor=ColorUtils.getColor(at.getColor());
fgcolor=ColorUtils.getOpposite(at.getColor());
} else {
bgcolor=Color.white;
fgcolor=Color.black;
}
big.setColor(bgcolor);
long start=(at.hasStart())?at.getStart():zoom.startInstant;
int x1 = zoom.convertInstantToPixel(start);
long stop=(at.hasStop())?at.getStop():zoom.stopInstant;
int x2 = zoom.convertInstantToPixel(stop);
if(x1<=0 && x2<0 )continue;
if(x1<0) x1=0;
int width=(x2 - x1 <= 1) ? 1 : x2 - x1 - 1;
big.fillRect(x1-leftDelta, 0, width, getHeight());
big.setColor(fgcolor);
big.setFont(f);
Rectangle2D bounds=f.getStringBounds(at.getName(), big.getFontRenderContext());
if(width>bounds.getWidth()) {
LineMetrics lm=f.getLineMetrics(at.getName(), big.getFontRenderContext());
big.drawString(at.getName(), x1-leftDelta+1, (int)lm.getAscent()+1);
}
big.setColor(Color.DARK_GRAY);
big.drawRect(x1-leftDelta, 0, width-1, getHeight()-1);
}
// border.paintBorder(this, big, 0, 0, getWidth(),getHeight() );
}
g.drawImage(image,0,0,this);
}
}