package NET.worlds.console; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Event; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Point; import java.awt.Scrollbar; import java.util.Vector; public class TreePanel extends ExposedPanel implements DialogDisabled { private static final long serialVersionUID = -5534860983354876334L; private Vector items = new Vector(); private boolean delayRepaints; private boolean needRepaint; private boolean needRecalc; private boolean needMakeVisible; private Scrollbar scrollbar = new WiderScrollbar(); private int scrollPos = 0; private int linesVisible; private int selectedIndex = -1; private boolean hasFocus = true; private Font nFont = new Font(Console.message("TreeFont"), 0, 13); private FontMetrics nFontMetrics = this.getFontMetrics(this.nFont); private Font bFont = new Font(Console.message("TreeFont"), 1, 11); private FontMetrics bFontMetrics = this.getFontMetrics(this.bFont); private int fontHeight = Math.max(this.nFontMetrics.getHeight(), this.bFontMetrics.getHeight()); private int itemHeight = this.fontHeight; private boolean isDialogDisabled; private static final int[] cxs = new int[]{0, 0, 6}; private static final int[] cys = new int[]{6, -6, 0}; private MoveablePolygon closedIcon = new MoveablePolygon(cxs, cys); private static final int[] oxs = new int[]{-5, 6, 0}; private static final int[] oys = new int[]{0, 0, 6}; private MoveablePolygon openedIcon = new MoveablePolygon(oxs, oys); private static final Color normBGColor = new Color(80, 80, 80); private static final Color normFGColor = new Color(190, 190, 190); private static final Color selFGColor = new Color(255, 255, 175); private static final int indentPixels = 14; public TreePanel() { this.setBackground(normBGColor); this.setLayout(new BorderLayout()); this.add("East", this.scrollbar); this.scrollbar.hide(); } public synchronized void delayRepaints(boolean state) { if (!(this.delayRepaints = state)) { if (this.needRecalc) { this.recalc(); } if (this.needMakeVisible) { this.makeVisible(this.selectedIndex); } if (this.needRepaint) { this.repaint(); } } } private synchronized void needRepaint(boolean needRecalc, boolean needMakeVisible) { this.needRepaint = true; this.needRecalc |= needRecalc; this.needMakeVisible |= needMakeVisible; this.delayRepaints(this.delayRepaints); } public synchronized int getSelectedIndex() { return this.selectedIndex; } public synchronized void select(int item) { this.selectedIndex = item; this.needRepaint(false, true); } public void setFocus(boolean hasFocus) { if (this.hasFocus != hasFocus) { this.hasFocus = hasFocus; this.needRepaint(false, false); } } @Override public boolean hasFocus() { return this.hasFocus; } @Override public synchronized void reshape(int x, int y, int w, int h) { super.reshape(x, y, w, h); this.needRepaint(true, false); } public synchronized TreeNode getSelectedNode() { return this.selectedIndex != -1 ? this.items.elementAt(this.selectedIndex) : null; } public synchronized void reset(Vector items) { this.items = items; this.needRepaint(true, false); } public synchronized void removeAllElements() { this.items.removeAllElements(); this.needRepaint(true, false); } public synchronized void insertElementAt(TreeNode ele, int index) { this.items.insertElementAt(ele, index); this.needRepaint(true, false); } public synchronized void removeElementAt(int index) { this.items.removeElementAt(index); this.needRepaint(true, false); } public synchronized void addElement(TreeNode ele) { this.items.addElement(ele); this.needRepaint(true, false); } public int countElements() { return this.items.size(); } public TreeNode elementAt(int index) { return this.items.elementAt(index); } public TreeNode elementAt(Point p) { int item = this.scrollPos + p.y / this.itemHeight; return item < this.items.size() ? this.items.elementAt(item) : null; } private void makeVisible(int item) { int count = this.items.size(); if (count > this.linesVisible && (item < this.scrollPos || item >= this.scrollPos + this.linesVisible)) { this.setScrollValue(Math.min(item * this.itemHeight, this.scrollbar.getMaximum())); } this.needMakeVisible = false; } private void add(GridBagLayout gbag, Component comp, GridBagConstraints c) { gbag.setConstraints(comp, c); this.add(comp); } public synchronized void recalc() { int height = this.getSize().height; this.linesVisible = Math.max(1, height / this.itemHeight); int count = this.items.size(); if (count > this.linesVisible) { if (!this.scrollbar.isVisible()) { this.scrollbar.setVisible(true); this.validate(); } this.scrollbar.setValues(this.scrollPos * this.itemHeight, this.linesVisible * this.itemHeight, 0, count * this.itemHeight); this.scrollbar.setPageIncrement(this.linesVisible * this.itemHeight); this.scrollbar.setLineIncrement(this.itemHeight); } else { if (this.scrollbar.isVisible()) { this.scrollbar.hide(); this.validate(); } this.scrollPos = 0; } this.needRecalc = false; } @Override public synchronized void paint(Graphics g) { super.paint(g); int width = this.getSize().width; int height = this.getSize().height; int count = this.items.size(); int y = 0; for (int i = this.scrollPos; i < count; i++) { TreeNode node = this.elementAt(i); String name = node.toString(); boolean isTitle = node.displayAsTitle(); Font font = this.nFont; FontMetrics metrics = this.nFontMetrics; if (isTitle) { font = this.bFont; metrics = this.bFontMetrics; } g.setFont(font); int ascent = metrics.getAscent(); int x = node.getLevel() * 14; g.setColor(normFGColor); if (node.isOpen()) { this.openedIcon.drawFilled(g, x + 5, y + ascent - 5); } else { this.closedIcon.drawFilled(g, x + 5, y + ascent - 5); } if (i == this.selectedIndex) { g.setColor(selFGColor); } g.drawString(name, x + 11 + 5, y + ascent); if (i == this.selectedIndex && this.hasFocus) { g.drawRect(x + 11 + 3, y, metrics.stringWidth(name) + 3, this.itemHeight); } y += this.itemHeight; if (y >= height) { break; } } this.needRepaint = false; } @Override public boolean handleEvent(Event e) { if (this.isDialogDisabled) { return false; } else { switch (e.id) { case 601: return this.scrollLineUp(); case 602: return this.scrollLineDown(); case 603: return this.scrollPageUp(); case 604: return this.scrollPageDown(); case 605: return this.scrollAbsolute(); default: return super.handleEvent(e); } } } @Override public void dialogDisable(boolean disable) { this.isDialogDisabled = disable; } @Override public synchronized boolean mouseDown(Event e, int x, int y) { this.setFocus(true); int item = this.scrollPos + y / this.itemHeight; if (item >= 0 && item < this.items.size()) { if (e.clickCount == 1) { this.treeSelect(item); int yStart = y / this.itemHeight * this.itemHeight; TreeNode node = this.elementAt(item); MoveablePolygon widget = node.isOpen() ? this.openedIcon : this.closedIcon; FontMetrics metrics = node.displayAsTitle() ? this.nFontMetrics : this.bFontMetrics; widget.moveTo(node.getLevel() * 14 + 5, yStart + metrics.getAscent() - 5); if (widget.getBoundingBox().inside(x, y)) { this.treeOpen(item); } } else { this.treeOpen(item); } } return true; } public void treeSelect(int item) { } public void treeOpen(int item) { } private boolean setScrollValue(int value) { value = Math.max(this.scrollbar.getMinimum(), value); value = Math.min(this.scrollbar.getMaximum(), value); this.scrollPos = value / this.itemHeight; this.scrollbar.setValue(this.scrollPos * this.itemHeight); this.needRepaint(false, false); return true; } private boolean scrollLineUp() { return this.setScrollValue(this.scrollPos * this.itemHeight - this.scrollbar.getLineIncrement()); } private boolean scrollLineDown() { return this.setScrollValue(this.scrollPos * this.itemHeight + this.scrollbar.getLineIncrement()); } private boolean scrollPageUp() { return this.setScrollValue(this.scrollPos * this.itemHeight - this.scrollbar.getPageIncrement()); } private boolean scrollPageDown() { return this.setScrollValue(this.scrollPos * this.itemHeight + this.scrollbar.getPageIncrement()); } private boolean scrollAbsolute() { return this.setScrollValue(this.scrollbar.getValue()); } }