package nodebox.ui;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import static com.google.common.base.Preconditions.checkArgument;
public class AddressBar extends JPanel implements MouseListener {
public static final Image addressGradient;
public static final Image addressArrow;
private static ImmutableList<String> ROOT_LIST = ImmutableList.of("root");
public static final int ADDRESS_BAR_HEIGHT = 25;
static {
try {
addressGradient = ImageIO.read(AddressBar.class.getResourceAsStream("/address-gradient.png"));
addressArrow = ImageIO.read(AddressBar.class.getResourceAsStream("/address-arrow.png"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private ImmutableList<String> segments = ImmutableList.of();
private int[] positions;
private int armed = -1;
private OnSegmentClickListener onSegmentClickListener;
private String message = "";
public AddressBar() {
addMouseListener(this);
setMinimumSize(new Dimension(0, ADDRESS_BAR_HEIGHT));
setMaximumSize(new Dimension(Integer.MAX_VALUE, ADDRESS_BAR_HEIGHT));
setPreferredSize(new Dimension(Integer.MAX_VALUE, ADDRESS_BAR_HEIGHT));
setLayout(null);
}
public ImmutableList<String> getSegments() {
return segments;
}
public void setSegments(Iterable<String> segments) {
this.segments = ImmutableList.copyOf(segments);
repaint();
}
public void setPath(String path) {
checkArgument(path.startsWith("/"), "Only absolute paths are supported.");
if (path.length() == 1) {
setSegments(ROOT_LIST);
} else {
setSegments(Iterables.concat(ROOT_LIST, Splitter.on("/").split(path.substring(1))));
}
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
repaint();
}
/**
* Returns the segment-click callback registered for this address bar.
*
* @return The callback, or null if one is not registered.
*/
public OnSegmentClickListener getOnSegmentClickListener() {
return onSegmentClickListener;
}
/**
* Register a callback to be invoked when a segment was clicked in the address bar.
*
* @param l the callback that will run.
*/
public void setOnSegmentClickListener(OnSegmentClickListener l) {
onSegmentClickListener = l;
}
@Override
protected void paintComponent(Graphics g) {
positions = new int[segments.size()];
Graphics2D g2 = (Graphics2D) g;
g2.setFont(Theme.SMALL_BOLD_FONT);
g2.drawImage(addressGradient, 0, 0, getWidth(), 25, null);
int x = 10;
int i = 0;
for (String segment : segments) {
if (i == armed) {
g2.setColor(Theme.TEXT_ARMED_COLOR);
} else {
g2.setColor(Theme.TEXT_NORMAL_COLOR);
}
SwingUtils.drawShadowText(g2, segment, x, 16);
int width = g2.getFontMetrics().stringWidth(segment);
x += width + 5;
positions[i] = x + 10;
g2.drawImage(addressArrow, x, 1, null);
x += 15;
i++;
}
if (! message.isEmpty()) {
g2.setColor(Theme.TEXT_NORMAL_COLOR);
int w = g2.getFontMetrics().stringWidth(message);
g2.drawString(message, getWidth() - w - 10, 15);
}
}
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
int mx = e.getX();
armed = partIndex(mx);
repaint();
}
public void mouseReleased(MouseEvent e) {
armed = -1;
int mx = e.getX();
int partIndex = partIndex(mx);
if (partIndex == -1) return;
String selectedPart = segments.get(partIndex);
if (selectedPart != null && onSegmentClickListener != null)
onSegmentClickListener.onSegmentClicked(pathForIndex(partIndex));
repaint();
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
armed = -1;
repaint();
}
private int partIndex(int x) {
if (positions == null) return -1;
for (int i = 0; i < positions.length; i++) {
if (x < positions[i])
return i;
}
return -1;
}
private String pathForIndex(int endIndex) {
return "/" + Joiner.on("/").join(segments.subList(1, endIndex + 1));
}
/**
* Callback listener to be invoked when an address segment has been clicked.
*/
public static interface OnSegmentClickListener {
/**
* Called when a part has been clicked.
*
* @param fullPath The full path of the part that was clicked.
*/
public void onSegmentClicked(String fullPath);
}
}