001: /* Copyright (c) 1995-2000, The Hypersonic SQL Group.
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the Hypersonic SQL Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: *
030: * This software consists of voluntary contributions made by many individuals
031: * on behalf of the Hypersonic SQL Group.
032: *
033: *
034: * For work added by the HSQL Development Group:
035: *
036: * Copyright (c) 2001-2005, The HSQL Development Group
037: * All rights reserved.
038: *
039: * Redistribution and use in source and binary forms, with or without
040: * modification, are permitted provided that the following conditions are met:
041: *
042: * Redistributions of source code must retain the above copyright notice, this
043: * list of conditions and the following disclaimer.
044: *
045: * Redistributions in binary form must reproduce the above copyright notice,
046: * this list of conditions and the following disclaimer in the documentation
047: * and/or other materials provided with the distribution.
048: *
049: * Neither the name of the HSQL Development Group nor the names of its
050: * contributors may be used to endorse or promote products derived from this
051: * software without specific prior written permission.
052: *
053: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
054: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
055: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
056: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
057: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
058: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
059: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
060: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
061: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
062: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
063: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
064: */
065:
066: package org.hsqldb.util;
067:
068: import java.util.Vector;
069:
070: import java.awt.Color;
071: import java.awt.Dimension;
072: import java.awt.Event;
073: import java.awt.Font;
074: import java.awt.FontMetrics;
075: import java.awt.Graphics;
076: import java.awt.Image;
077: import java.awt.Panel;
078: import java.awt.Scrollbar;
079: import java.awt.SystemColor;
080: import java.awt.Toolkit;
081:
082: /**
083: *
084: * @author Thomas Mueller (Hypersonic SQL Group)
085: * @version 1.7.0
086: * @since Hypersonic SQL
087: */
088: class Tree extends Panel {
089:
090: // static
091: private static Font fFont;
092: private static FontMetrics fMetrics;
093: private static int iRowHeight;
094: private static int iIndentWidth;
095: private int iMaxTextLength;
096:
097: // drawing
098: private Dimension dMinimum;
099: private Graphics gImage;
100: private Image iImage;
101:
102: // height / width
103: private int iWidth, iHeight;
104: private int iFirstRow;
105: private int iTreeWidth, iTreeHeight;
106: private int iX, iY;
107:
108: // data
109: private Vector vData;
110: private int iRowCount;
111:
112: // scrolling
113: private Scrollbar sbHoriz, sbVert;
114: private int iSbWidth, iSbHeight;
115:
116: static {
117: fFont = new Font("Dialog", Font.PLAIN, 12);
118: fMetrics = Toolkit.getDefaultToolkit().getFontMetrics(fFont);
119: iRowHeight = getMaxHeight(fMetrics);
120: iIndentWidth = 12;
121: }
122:
123: /**
124: * Constructor declaration
125: *
126: */
127: Tree() {
128:
129: super ();
130:
131: vData = new Vector();
132:
133: setLayout(null);
134:
135: sbHoriz = new Scrollbar(Scrollbar.HORIZONTAL);
136:
137: add(sbHoriz);
138:
139: sbVert = new Scrollbar(Scrollbar.VERTICAL);
140:
141: add(sbVert);
142: }
143:
144: /**
145: * Method declaration
146: *
147: *
148: * @param d
149: */
150: public void setMinimumSize(Dimension d) {
151: dMinimum = d;
152: }
153:
154: /**
155: * Method declaration
156: *
157: *
158: * @param x
159: * @param y
160: * @param w
161: * @param h
162: */
163:
164: // fredt@users 20011210 - patch 450412 by elise@users
165: // with additional replacement of deprecated methods
166: public void setBounds(int x, int y, int w, int h) {
167:
168: super .setBounds(x, y, w, h);
169:
170: iSbHeight = sbHoriz.getPreferredSize().height;
171: iSbWidth = sbVert.getPreferredSize().width;
172: iHeight = h - iSbHeight;
173: iWidth = w - iSbWidth;
174:
175: sbHoriz.setBounds(0, iHeight, iWidth, iSbHeight);
176: sbVert.setBounds(iWidth, 0, iSbWidth, iHeight);
177: adjustScroll();
178:
179: iImage = null;
180:
181: repaint();
182: }
183:
184: /**
185: * Method declaration
186: *
187: */
188: public void removeAll() {
189:
190: vData = new Vector();
191: iRowCount = 0;
192:
193: adjustScroll();
194:
195: iMaxTextLength = 10;
196:
197: repaint();
198: }
199:
200: /**
201: * Method declaration
202: *
203: *
204: * @param key
205: * @param value
206: * @param state
207: * @param color
208: */
209: public void addRow(String key, String value, String state, int color) {
210:
211: String[] row = new String[4];
212:
213: if (value == null) {
214: value = "";
215: }
216:
217: row[0] = key;
218: row[1] = value;
219: row[2] = state; // null / "-" / "+"
220: row[3] = String.valueOf(color);
221:
222: vData.addElement(row);
223:
224: int len = fMetrics.stringWidth(value);
225:
226: if (len > iMaxTextLength) {
227: iMaxTextLength = len;
228: }
229:
230: iRowCount++;
231: }
232:
233: /**
234: * Method declaration
235: *
236: *
237: * @param key
238: * @param value
239: */
240: public void addRow(String key, String value) {
241: addRow(key, value, null, 0);
242: }
243:
244: /**
245: * Method declaration
246: *
247: */
248: public void update() {
249: adjustScroll();
250: repaint();
251: }
252:
253: /**
254: * Method declaration
255: *
256: */
257: void adjustScroll() {
258:
259: iTreeHeight = iRowHeight * (iRowCount + 1);
260:
261: // correct would be iMaxTextLength + iMaxIndent*iIndentWidth
262: iTreeWidth = iMaxTextLength * 2;
263:
264: sbHoriz.setValues(iX, iWidth, 0, iTreeWidth);
265:
266: int v = iY / iRowHeight, h = iHeight / iRowHeight;
267:
268: sbVert.setValues(v, h, 0, iRowCount + 1);
269:
270: iX = sbHoriz.getValue();
271: iY = iRowHeight * sbVert.getValue();
272: }
273:
274: /**
275: * Method declaration
276: *
277: *
278: * @param e
279: *
280: * @return
281: */
282:
283: // fredt@users 20020130 - comment by fredt
284: // to remove this deprecated method we need to rewrite the Tree class as a
285: // ScrollPane component
286: public boolean handleEvent(Event e) {
287:
288: switch (e.id) {
289:
290: case Event.SCROLL_LINE_UP:
291: case Event.SCROLL_LINE_DOWN:
292: case Event.SCROLL_PAGE_UP:
293: case Event.SCROLL_PAGE_DOWN:
294: case Event.SCROLL_ABSOLUTE:
295: iX = sbHoriz.getValue();
296: iY = iRowHeight * sbVert.getValue();
297:
298: repaint();
299:
300: return true;
301: }
302:
303: return super .handleEvent(e);
304: }
305:
306: /**
307: * Method declaration
308: *
309: *
310: * @param g
311: */
312: public void paint(Graphics g) {
313:
314: if (g == null || iWidth <= 0 || iHeight <= 0) {
315: return;
316: }
317:
318: g.setColor(SystemColor.control);
319: g.fillRect(iWidth, iHeight, iSbWidth, iSbHeight);
320:
321: if (iImage == null) {
322: iImage = createImage(iWidth, iHeight);
323: gImage = iImage.getGraphics();
324:
325: gImage.setFont(fFont);
326: }
327:
328: gImage.setColor(Color.white);
329: gImage.fillRect(0, 0, iWidth, iHeight);
330:
331: int[] lasty = new int[100];
332: String[] root = new String[100];
333:
334: root[0] = "";
335:
336: int currentindent = 0;
337: int y = iRowHeight;
338:
339: y -= iY;
340:
341: boolean closed = false;
342:
343: for (int i = 0; i < iRowCount; i++) {
344: String[] s = (String[]) vData.elementAt(i);
345: String key = s[0];
346: String data = s[1];
347: String folder = s[2];
348: int ci = currentindent;
349:
350: for (; ci > 0; ci--) {
351: if (key.startsWith(root[ci])) {
352: break;
353: }
354: }
355:
356: if (root[ci].length() < key.length()) {
357: ci++;
358: }
359:
360: if (closed && ci > currentindent) {
361: continue;
362: }
363:
364: closed = folder != null && folder.equals("+");
365: root[ci] = key;
366:
367: int x = iIndentWidth * ci - iX;
368:
369: gImage.setColor(Color.lightGray);
370: gImage.drawLine(x, y, x + iIndentWidth, y);
371: gImage.drawLine(x, y, x, lasty[ci]);
372:
373: lasty[ci + 1] = y;
374:
375: int py = y + iRowHeight / 3;
376: int px = x + iIndentWidth * 2;
377:
378: if (folder != null) {
379: lasty[ci + 1] += 4;
380:
381: int rgb = Integer.parseInt(s[3]);
382:
383: gImage
384: .setColor(rgb == 0 ? Color.white : new Color(
385: rgb));
386: gImage.fillRect(x + iIndentWidth - 3, y - 3, 7, 7);
387: gImage.setColor(Color.black);
388: gImage.drawRect(x + iIndentWidth - 4, y - 4, 8, 8);
389: gImage.drawLine(x + iIndentWidth - 2, y, x
390: + iIndentWidth + 2, y);
391:
392: if (folder.equals("+")) {
393: gImage.drawLine(x + iIndentWidth, y - 2, x
394: + iIndentWidth, y + 2);
395: }
396: } else {
397: px -= iIndentWidth;
398: }
399:
400: gImage.setColor(Color.black);
401: gImage.drawString(data, px, py);
402:
403: currentindent = ci;
404: y += iRowHeight;
405: }
406:
407: g.drawImage(iImage, 0, 0, this );
408: }
409:
410: /**
411: * Method declaration
412: *
413: *
414: * @param g
415: */
416: public void update(Graphics g) {
417: paint(g);
418: }
419:
420: /**
421: * Method declaration
422: *
423: *
424: * @return
425: */
426: public Dimension preferredSize() {
427: return dMinimum;
428: }
429:
430: /**
431: * Method declaration
432: *
433: *
434: * @return
435: */
436: public Dimension getPreferredSize() {
437: return dMinimum;
438: }
439:
440: /**
441: * Method declaration
442: *
443: *
444: * @return
445: */
446: public Dimension getMinimumSize() {
447: return dMinimum;
448: }
449:
450: /**
451: * Method declaration
452: *
453: *
454: * @return
455: */
456: public Dimension minimumSize() {
457: return dMinimum;
458: }
459:
460: /**
461: * Method declaration
462: *
463: *
464: * @param e
465: * @param x
466: * @param y
467: *
468: * @return
469: */
470: public boolean mouseDown(Event e, int x, int y) {
471:
472: if (iRowHeight == 0 || x > iWidth || y > iHeight) {
473: return true;
474: }
475:
476: y += iRowHeight / 2;
477:
478: String[] root = new String[100];
479:
480: root[0] = "";
481:
482: int currentindent = 0;
483: int cy = iRowHeight;
484: boolean closed = false;
485: int i = 0;
486:
487: y += iY;
488:
489: for (; i < iRowCount; i++) {
490: String[] s = (String[]) vData.elementAt(i);
491: String key = s[0];
492: String folder = s[2];
493: int ci = currentindent;
494:
495: for (; ci > 0; ci--) {
496: if (key.startsWith(root[ci])) {
497: break;
498: }
499: }
500:
501: if (root[ci].length() < key.length()) {
502: ci++;
503: }
504:
505: if (closed && ci > currentindent) {
506: continue;
507: }
508:
509: if (cy <= y && cy + iRowHeight > y) {
510: break;
511: }
512:
513: root[ci] = key;
514: closed = folder != null && folder.equals("+");
515: currentindent = ci;
516: cy += iRowHeight;
517: }
518:
519: if (i >= 0 && i < iRowCount) {
520: String[] s = (String[]) vData.elementAt(i);
521: String folder = s[2];
522:
523: if (folder != null && folder.equals("+")) {
524: folder = "-";
525: } else if (folder != null && folder.equals("-")) {
526: folder = "+";
527: }
528:
529: s[2] = folder;
530:
531: vData.setElementAt(s, i);
532: repaint();
533: }
534:
535: return true;
536: }
537:
538: /**
539: * Method declaration
540: *
541: *
542: * @param f
543: *
544: * @return
545: */
546: private static int getMaxHeight(FontMetrics f) {
547: return f.getHeight() + 2;
548: }
549: }
|