| /* |
| * For work developed by the HSQL Development Group: |
| * |
| * Copyright (c) 2001-2010, The HSQL Development Group |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * Neither the name of the HSQL Development Group nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, |
| * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * |
| * |
| * For work originally developed by the Hypersonic SQL Group: |
| * |
| * Copyright (c) 1995-2000, The Hypersonic SQL Group. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * Neither the name of the Hypersonic SQL Group nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP, |
| * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * This software consists of voluntary contributions made by many individuals |
| * on behalf of the Hypersonic SQL Group. |
| */ |
| |
| |
| package org.hsqldb.util; |
| |
| import java.util.Vector; |
| |
| import java.awt.Color; |
| import java.awt.Dimension; |
| import java.awt.Event; |
| import java.awt.Font; |
| import java.awt.FontMetrics; |
| import java.awt.Graphics; |
| import java.awt.Image; |
| import java.awt.Panel; |
| import java.awt.Scrollbar; |
| import java.awt.SystemColor; |
| import java.awt.Toolkit; |
| |
| /** |
| * |
| * @author Thomas Mueller (Hypersonic SQL Group) |
| * @version 1.7.0 |
| * @since Hypersonic SQL |
| */ |
| class Tree extends Panel { |
| |
| // static |
| private static Font fFont; |
| private static FontMetrics fMetrics; |
| private static int iRowHeight; |
| private static int iIndentWidth; |
| private int iMaxTextLength; |
| |
| // drawing |
| private Dimension dMinimum; |
| private Graphics gImage; |
| private Image iImage; |
| |
| // height / width |
| private int iWidth, iHeight; |
| private int iFirstRow; |
| private int iTreeWidth, iTreeHeight; |
| private int iX, iY; |
| |
| // data |
| private Vector vData; |
| private int iRowCount; |
| |
| // scrolling |
| private Scrollbar sbHoriz, sbVert; |
| private int iSbWidth, iSbHeight; |
| |
| static { |
| fFont = new Font("Dialog", Font.PLAIN, 12); |
| fMetrics = Toolkit.getDefaultToolkit().getFontMetrics(fFont); |
| iRowHeight = getMaxHeight(fMetrics); |
| iIndentWidth = 12; |
| } |
| |
| /** |
| * Constructor declaration |
| * |
| */ |
| Tree() { |
| |
| super(); |
| |
| vData = new Vector(); |
| |
| setLayout(null); |
| |
| sbHoriz = new Scrollbar(Scrollbar.HORIZONTAL); |
| |
| add(sbHoriz); |
| |
| sbVert = new Scrollbar(Scrollbar.VERTICAL); |
| |
| add(sbVert); |
| } |
| |
| /** |
| * Method declaration |
| * |
| * |
| * @param d |
| */ |
| public void setMinimumSize(Dimension d) { |
| dMinimum = d; |
| } |
| |
| /** |
| * Method declaration |
| * |
| * |
| * @param x |
| * @param y |
| * @param w |
| * @param h |
| */ |
| |
| // fredt@users 20011210 - patch 450412 by elise@users |
| // with additional replacement of deprecated methods |
| public void setBounds(int x, int y, int w, int h) { |
| |
| super.setBounds(x, y, w, h); |
| |
| iSbHeight = sbHoriz.getPreferredSize().height; |
| iSbWidth = sbVert.getPreferredSize().width; |
| iHeight = h - iSbHeight; |
| iWidth = w - iSbWidth; |
| |
| sbHoriz.setBounds(0, iHeight, iWidth, iSbHeight); |
| sbVert.setBounds(iWidth, 0, iSbWidth, iHeight); |
| adjustScroll(); |
| |
| iImage = null; |
| |
| repaint(); |
| } |
| |
| /** |
| * Method declaration |
| * |
| */ |
| public void removeAll() { |
| |
| vData = new Vector(); |
| iRowCount = 0; |
| |
| adjustScroll(); |
| |
| iMaxTextLength = 10; |
| |
| repaint(); |
| } |
| |
| /** |
| * Method declaration |
| * |
| * |
| * @param key |
| * @param value |
| * @param state |
| * @param color |
| */ |
| public void addRow(String key, String value, String state, int color) { |
| |
| String[] row = new String[4]; |
| |
| if (value == null) { |
| value = ""; |
| } |
| |
| row[0] = key; |
| row[1] = value; |
| row[2] = state; // null / "-" / "+" |
| row[3] = String.valueOf(color); |
| |
| vData.addElement(row); |
| |
| int len = fMetrics.stringWidth(value); |
| |
| if (len > iMaxTextLength) { |
| iMaxTextLength = len; |
| } |
| |
| iRowCount++; |
| } |
| |
| /** |
| * Method declaration |
| * |
| * |
| * @param key |
| * @param value |
| */ |
| public void addRow(String key, String value) { |
| addRow(key, value, null, 0); |
| } |
| |
| /** |
| * Method declaration |
| * |
| */ |
| public void update() { |
| adjustScroll(); |
| repaint(); |
| } |
| |
| /** |
| * Method declaration |
| * |
| */ |
| void adjustScroll() { |
| |
| iTreeHeight = iRowHeight * (iRowCount + 1); |
| |
| // correct would be iMaxTextLength + iMaxIndent*iIndentWidth |
| iTreeWidth = iMaxTextLength * 2; |
| |
| sbHoriz.setValues(iX, iWidth, 0, iTreeWidth); |
| |
| int v = iY / iRowHeight, |
| h = iHeight / iRowHeight; |
| |
| sbVert.setValues(v, h, 0, iRowCount + 1); |
| |
| iX = sbHoriz.getValue(); |
| iY = iRowHeight * sbVert.getValue(); |
| } |
| |
| /** |
| * Method declaration |
| * |
| * |
| * @param e |
| */ |
| |
| // fredt@users 20020130 - comment by fredt |
| // to remove this deprecated method we need to rewrite the Tree class as a |
| // ScrollPane component |
| public boolean handleEvent(Event e) { |
| |
| switch (e.id) { |
| |
| case Event.SCROLL_LINE_UP : |
| case Event.SCROLL_LINE_DOWN : |
| case Event.SCROLL_PAGE_UP : |
| case Event.SCROLL_PAGE_DOWN : |
| case Event.SCROLL_ABSOLUTE : |
| iX = sbHoriz.getValue(); |
| iY = iRowHeight * sbVert.getValue(); |
| |
| repaint(); |
| |
| return true; |
| } |
| |
| return super.handleEvent(e); |
| } |
| |
| /** |
| * Method declaration |
| * |
| * |
| * @param g |
| */ |
| public void paint(Graphics g) { |
| |
| if (g == null || iWidth <= 0 || iHeight <= 0) { |
| return; |
| } |
| |
| g.setColor(SystemColor.control); |
| g.fillRect(iWidth, iHeight, iSbWidth, iSbHeight); |
| |
| if (iImage == null) { |
| iImage = createImage(iWidth, iHeight); |
| gImage = iImage.getGraphics(); |
| |
| gImage.setFont(fFont); |
| } |
| |
| gImage.setColor(Color.white); |
| gImage.fillRect(0, 0, iWidth, iHeight); |
| |
| int[] lasty = new int[100]; |
| String[] root = new String[100]; |
| |
| root[0] = ""; |
| |
| int currentindent = 0; |
| int y = iRowHeight; |
| |
| y -= iY; |
| |
| boolean closed = false; |
| |
| for (int i = 0; i < iRowCount; i++) { |
| String[] s = (String[]) vData.elementAt(i); |
| String key = s[0]; |
| String data = s[1]; |
| String folder = s[2]; |
| int ci = currentindent; |
| |
| for (; ci > 0; ci--) { |
| if (key.startsWith(root[ci])) { |
| break; |
| } |
| } |
| |
| if (root[ci].length() < key.length()) { |
| ci++; |
| } |
| |
| if (closed && ci > currentindent) { |
| continue; |
| } |
| |
| closed = folder != null && folder.equals("+"); |
| root[ci] = key; |
| |
| int x = iIndentWidth * ci - iX; |
| |
| gImage.setColor(Color.lightGray); |
| gImage.drawLine(x, y, x + iIndentWidth, y); |
| gImage.drawLine(x, y, x, lasty[ci]); |
| |
| lasty[ci + 1] = y; |
| |
| int py = y + iRowHeight / 3; |
| int px = x + iIndentWidth * 2; |
| |
| if (folder != null) { |
| lasty[ci + 1] += 4; |
| |
| int rgb = Integer.parseInt(s[3]); |
| |
| gImage.setColor(rgb == 0 ? Color.white |
| : new Color(rgb)); |
| gImage.fillRect(x + iIndentWidth - 3, y - 3, 7, 7); |
| gImage.setColor(Color.black); |
| gImage.drawRect(x + iIndentWidth - 4, y - 4, 8, 8); |
| gImage.drawLine(x + iIndentWidth - 2, y, |
| x + iIndentWidth + 2, y); |
| |
| if (folder.equals("+")) { |
| gImage.drawLine(x + iIndentWidth, y - 2, |
| x + iIndentWidth, y + 2); |
| } |
| } else { |
| px -= iIndentWidth; |
| } |
| |
| gImage.setColor(Color.black); |
| gImage.drawString(data, px, py); |
| |
| currentindent = ci; |
| y += iRowHeight; |
| } |
| |
| g.drawImage(iImage, 0, 0, this); |
| } |
| |
| /** |
| * Method declaration |
| * |
| * |
| * @param g |
| */ |
| public void update(Graphics g) { |
| paint(g); |
| } |
| |
| /** |
| * Method declaration |
| */ |
| public Dimension preferredSize() { |
| return dMinimum; |
| } |
| |
| /** |
| * Method declaration |
| */ |
| public Dimension getPreferredSize() { |
| return dMinimum; |
| } |
| |
| /** |
| * Method declaration |
| */ |
| public Dimension getMinimumSize() { |
| return dMinimum; |
| } |
| |
| /** |
| * Method declaration |
| */ |
| public Dimension minimumSize() { |
| return dMinimum; |
| } |
| |
| /** |
| * Method declaration |
| * |
| * |
| * @param e |
| * @param x |
| * @param y |
| */ |
| public boolean mouseDown(Event e, int x, int y) { |
| |
| if (iRowHeight == 0 || x > iWidth || y > iHeight) { |
| return true; |
| } |
| |
| y += iRowHeight / 2; |
| |
| String[] root = new String[100]; |
| |
| root[0] = ""; |
| |
| int currentindent = 0; |
| int cy = iRowHeight; |
| boolean closed = false; |
| int i = 0; |
| |
| y += iY; |
| |
| for (; i < iRowCount; i++) { |
| String[] s = (String[]) vData.elementAt(i); |
| String key = s[0]; |
| String folder = s[2]; |
| int ci = currentindent; |
| |
| for (; ci > 0; ci--) { |
| if (key.startsWith(root[ci])) { |
| break; |
| } |
| } |
| |
| if (root[ci].length() < key.length()) { |
| ci++; |
| } |
| |
| if (closed && ci > currentindent) { |
| continue; |
| } |
| |
| if (cy <= y && cy + iRowHeight > y) { |
| break; |
| } |
| |
| root[ci] = key; |
| closed = folder != null && folder.equals("+"); |
| currentindent = ci; |
| cy += iRowHeight; |
| } |
| |
| if (i >= 0 && i < iRowCount) { |
| String[] s = (String[]) vData.elementAt(i); |
| String folder = s[2]; |
| |
| if (folder != null && folder.equals("+")) { |
| folder = "-"; |
| } else if (folder != null && folder.equals("-")) { |
| folder = "+"; |
| } |
| |
| s[2] = folder; |
| |
| vData.setElementAt(s, i); |
| repaint(); |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Method declaration |
| * |
| * |
| * @param f |
| */ |
| private static int getMaxHeight(FontMetrics f) { |
| return f.getHeight() + 2; |
| } |
| } |