package org.yamcs.ui.archivebrowser;
import org.yamcs.protobuf.Yamcs.ArchiveTag;
import org.yamcs.utils.TimeEncoding;
import javax.swing.*;
import java.awt.*;
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.List;
import java.util.ListIterator;
public class TagBox extends Box implements MouseListener {
private static final long serialVersionUID = 1L;
private DataView dataView;
boolean drawPreviewLocator;
long startLocator, stopLocator, currentLocator;
final long DO_NOT_DRAW = Long.MIN_VALUE;
JLabel tagLabelItem;
JPopupMenu editTagPopup, newTagPopup;
JMenuItem removeTagMenuItem, editTagMenuItem;
int selectedRow=-1, selectedIndex=-1;
List<List<ArchiveTag>> tags=new ArrayList<List<ArchiveTag>>();//all tags loaded from yarch
TagEditDialog tagEditDialog;
ZoomSpec zoom;
private void buildTagEditDialog() {
if(tagEditDialog==null) {
tagEditDialog=new TagEditDialog(null);
tagEditDialog.setModal(true);
}
}
TagBox(DataView dataView) {
super(BoxLayout.PAGE_AXIS);
this.dataView = dataView;
startLocator = stopLocator = currentLocator = DO_NOT_DRAW;
drawPreviewLocator = false;
setOpaque(false);
ToolTipManager ttmgr = ToolTipManager.sharedInstance();
ttmgr.setInitialDelay(0);
ttmgr.setReshowDelay(0);
ttmgr.setDismissDelay(Integer.MAX_VALUE);
buildPopup();
addMouseListener(this);
/*
insertTag(ArchiveTag.newBuilder().setName("plus infinity").setStart(450).build());
insertTag(ArchiveTag.newBuilder().setName("cucucurigo long laaaaaaabel").setStart(100).setStop(300).setColor("red").build());
insertTag(ArchiveTag.newBuilder().setName("tag2").setStart(TimeEncoding.parse("2009-04-15T05:18:00")).setStop(TimeEncoding.parse("2009-06-03T18:40:37")).setColor("blue").build());
insertTag(ArchiveTag.newBuilder().setName("minus infinity").setStop(150).build());
insertTag(ArchiveTag.newBuilder().setName("plus infinity").setStart(450).build());
// insertTag(ArchiveTag.newBuilder().setName("plus infinity").setStart(450).build());
insertTag(ArchiveTag.newBuilder().setName("tag3").setStart(TimeEncoding.parse("2009-07-07T12:29:44")).setStop(TimeEncoding.parse("2009-07-07T13:28:26")).setColor("orange").build());
*/
}
/**
* insert a tag in tags, in order ensuring no overlap.
* @param tag
*/
private void insertTag(ArchiveTag tag) {
boolean inserted=false;
for(List<ArchiveTag> atl:tags) {
int min=0, max=atl.size()-1;
while(min<=max) {
int mid=(min+max)>>1;
ArchiveTag midtag=atl.get(mid);
if(tag.hasStop() && midtag.hasStart() && tag.getStop()<midtag.getStart()) {
max=mid-1;
} else if(tag.hasStart() && midtag.hasStop() && tag.getStart()>midtag.getStop()) {
min=mid+1;
} else {
break; //overlap
}
}
if(min>max) {
atl.add(min,tag);
inserted=true;
break;
}
}
if(!inserted) {
List<ArchiveTag> atl=new ArrayList<ArchiveTag>();
atl.add(tag);
tags.add(atl);
}
}
protected void buildPopup() {
editTagPopup = new JPopupMenu();
tagLabelItem = new JLabel();
tagLabelItem.setEnabled(false);
Box hbox = Box.createHorizontalBox();
hbox.add(Box.createHorizontalGlue());
hbox.add(tagLabelItem);
hbox.add(Box.createHorizontalGlue());
editTagPopup.insert(hbox, 0);
editTagPopup.addSeparator();
editTagMenuItem = new JMenuItem("Edit Tag");
editTagMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
buildTagEditDialog();
ArchiveTag selectedTag=tags.get(selectedRow).get(selectedIndex);
tagEditDialog.fillFrom(selectedTag);
tagEditDialog.setVisible(true);
if(tagEditDialog.ok) {
dataView.emitActionEvent(new TagEvent(this, "update-tag", selectedTag, tagEditDialog.getTag()));
}
}
});
editTagPopup.add(editTagMenuItem);
removeTagMenuItem = new JMenuItem("Remove Tag");
removeTagMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ArchiveTag selectedTag=tags.get(selectedRow).get(selectedIndex);
int answer=JOptionPane.showConfirmDialog(null, "Remove "+selectedTag.getName()+" ?", "Are you sure?", JOptionPane.YES_NO_OPTION);
if(answer==JOptionPane.YES_OPTION) {
dataView.emitActionEvent(new TagEvent(this, "delete-tag", selectedTag, null));
}
}
});
editTagPopup.add(removeTagMenuItem);
newTagPopup = new JPopupMenu();
JMenuItem newTagMenuItem = new JMenuItem("New Tag");
newTagMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
buildTagEditDialog();
tagEditDialog.setVisible(true);
if(tagEditDialog.ok) {
dataView.emitActionEvent(new TagEvent(this, "insert-tag", null, tagEditDialog.getTag()));
}
}
});
newTagPopup.add(newTagMenuItem);
}
public void createNewTag(long start, long stop) {
buildTagEditDialog();
tagEditDialog.startTextField.setValue(start);
tagEditDialog.stopTextField.setValue(stop);
tagEditDialog.setVisible(true);
if(tagEditDialog.ok) {
dataView.emitActionEvent(new TagEvent(this, "insert-tag", null, tagEditDialog.getTag()));
}
}
public void doMousePressed(MouseEvent e, int row, int index) {
selectedRow=row;
selectedIndex=index;
if(e.isPopupTrigger()) {
showPopup(e);
} else if(e.getButton()==MouseEvent.BUTTON1 && selectedRow!=-1 && selectedIndex !=-1) {
dataView.selectedTag(tags.get(selectedRow).get(selectedIndex));
}
}
void showPopup(final MouseEvent e) {
if(selectedIndex!=-1) {
ArchiveTag selectedTag=tags.get(selectedRow).get(selectedIndex);
tagLabelItem.setText(selectedTag.getName());
editTagPopup.validate();
editTagPopup.show(e.getComponent(), e.getX(), e.getY());
} else {
newTagPopup.validate();
newTagPopup.show(e.getComponent(), e.getX(), e.getY());
}
}
/*
void hidePopup(final MouseEvent e) {
if(true)return;
if (e.isPopupTrigger()) {
if ((packetPopup != null) && (popupLabelItem != null)) {
popupLabelItem.setVisible(false);
removePayloadMenuItem.setVisible(false);
removePacketMenuItem.setVisible(false);
removeExceptPacketMenuItem.setVisible(false);
copyOpsnameMenuItem.setVisible(false);
changeColorMenuItem.setVisible(false);
packetPopup.validate();
packetPopup.show(e.getComponent(), e.getX(), e.getY());
}
}
}
*/
@Override
public Point getToolTipLocation(MouseEvent event) {
return new Point(event.getX() - 94, event.getY() + 20);
}
void setToZoom(ZoomSpec zoom) {
this.zoom=zoom;
redrawTags();
}
void redrawTags() { // Draw reverse, so that 'most' tags stick to scale
removeAll();
if(!tags.isEmpty()) {
int row=tags.size()-1;
Insets in=this.getInsets();
for(ListIterator<List<ArchiveTag>> it=tags.listIterator(tags.size()); it.hasPrevious();) {
List<ArchiveTag> lat=it.previous();
TagTimeline tt=new TagTimeline(this, lat, zoom, row--, in.left);
add(tt);
}
}
revalidate();
repaint();
}
@Override
public void mousePressed(MouseEvent e) {
doMousePressed(e, -1, -1);
}
@Override public void mouseReleased(MouseEvent e) {}
@Override public void mouseExited(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent e) {}
@Override public void mouseClicked(MouseEvent e) {}
static public void main(String[] args) {
TimeEncoding.setUp();
JFrame frame=new JFrame();
frame.setSize(new Dimension(1000,100));
TagBox atb=new TagBox(null);
/* atb.insertTag(ArchiveTag.newBuilder().setName("plus infinity").setStart(450).build());
atb.insertTag(ArchiveTag.newBuilder().setName("cucucurigo long laaaaaaabel").setStart(100).setStop(300).setColor("red").build());
atb.insertTag(ArchiveTag.newBuilder().setName("tag2").setStart(500).setStop(700).setColor("blue").build());
atb.insertTag(ArchiveTag.newBuilder().setName("minus infinity").setStop(150).build());
atb.insertTag(ArchiveTag.newBuilder().setName("plus infinity").setStart(450).build());
atb.insertTag(ArchiveTag.newBuilder().setName("tag3").setStart(701).setStop(800).setColor("orange").build());
atb.insertTag(ArchiveTag.newBuilder().setName("tag4").setStart(800).setStop(900).setColor("magenta").build());
atb.insertTag(ArchiveTag.newBuilder().setName("tag5").setStart(801).setStop(1000).setColor("gray").build());
*/
atb.setToZoom(new ZoomSpec(0, 3600*1000, 3600*1000, 3*3600*1000));
frame.add(atb);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
public void addTags(List<ArchiveTag> tagList) {
for(ArchiveTag tag:tagList) {
insertTag(tag);
}
if (!dataView.zoomStack.empty()) {
redrawTags();
}
}
public void addTag(ArchiveTag tag) {
insertTag(tag);
redrawTags();
}
public void removeTag(ArchiveTag rtag) {
long t=rtag.hasStart()?rtag.getStart():rtag.getStop();
for(List<ArchiveTag> tagList:tags) {
int id=TagTimeline.time2Tag(tagList, t);
if(id!=-1) {
if(rtag.equals(tagList.get(id))) {
tagList.remove(id);
if(tagList.isEmpty()) tags.remove(tagList);
redrawTags();
return;
}
}
}
JOptionPane.showMessageDialog(null, "Could not find "+rtag.toString()+" to remove");
}
public void updateTag(ArchiveTag oldTag, ArchiveTag newTag) {
long t=oldTag.hasStart()?oldTag.getStart():oldTag.getStop();
for(List<ArchiveTag> tagList:tags) {
int id=TagTimeline.time2Tag(tagList, t);
if(id!=-1) {
if(oldTag.equals(tagList.get(id))) {
tagList.remove(id);
insertTag(newTag);
redrawTags();
return;
}
}
}
JOptionPane.showMessageDialog(null, "Could not find "+oldTag.toString()+" to remove");
}
static public class TagEvent extends ActionEvent {
public ArchiveTag newTag;
public ArchiveTag oldTag;
public TagEvent(Object source, String command, ArchiveTag oldTag, ArchiveTag newTag) {
super(source, ActionEvent.ACTION_PERFORMED, command);
this.newTag=newTag;
this.oldTag=oldTag;
}
}
}