package org.basex.gui.view.tree;
import java.awt.Graphics;
import org.basex.core.Context;
import org.basex.data.Data;
import org.basex.gui.layout.BaseXLayout;
import org.basex.gui.view.ViewData;
/**
* This class stores the rectangles.
*
* @author BaseX Team 2005-12, BSD License
* @author Wolfgang Miller
*/
final class TreeRects implements TreeConstants {
/** Saved rectangles. */
private TreeRect[][][] rects;
/**
* Create new rectangles and set subtree borders.
* @param sub subtree
* @param g graphics reference
* @param c context
* @param ds draw start
* @param dw draw width
* @param slim slim to text
* @return tree distance
*/
double generateRects(final TreeSubtree sub, final Graphics g,
final Context c, final int ds, final int dw, final boolean slim) {
final int[] roots = c.current().list;
final int rl = roots.length;
if(rl == 0) return 0;
final double w = (dw - BORDER_PADDING - ds) / (double) rl;
if(w < 2) {
return -1;
}
rects = new TreeRect[rl][][];
for(int i = 0; i < rl; ++i) {
generateRects(sub, c, g, i, ds, w, slim);
}
return w;
}
/**
* Generates cached rectangles.
*
* @param sub subtree
* @param c context
* @param g graphics reference
* @param rn root number
* @param ds draw start
* @param dw draw width
* @param slim slim to text
*/
private void generateRects(final TreeSubtree sub, final Context c,
final Graphics g, final int rn, final int ds, final double dw,
final boolean slim) {
final int h = sub.getSubtreeHeight(rn);
rects[rn] = new TreeRect[h][];
for(int lv = 0; lv < h; ++lv) {
final double w = dw / sub.levelSize(rn, lv);
if(w < 2) {
bigRectangle(rn, lv, ds, dw);
} else {
normalRectangle(sub, c, g, rn, lv, ds, w, slim);
}
}
}
/**
* Invoked if not enough space for more than one big rectangle.
* @param rn root
* @param lv level
* @param ds draw start
* @param w the width
*/
private void bigRectangle(final int rn, final int lv, final int ds,
final double w) {
rects[rn][lv] = new TreeRect[1];
rects[rn][lv][0] = new TreeRect((int) (w * rn) + BORDER_PADDING + ds,
(int) w - BORDER_PADDING);
}
/**
* Creates normal rectangles.
* @param sub subtree
* @param g graphics reference
* @param rn root
* @param lv level
* @param c context
* @param ds draw start
* @param w width
* @param slim slim to text
*/
private void normalRectangle(final TreeSubtree sub, final Context c,
final Graphics g, final int rn, final int lv, final int ds,
final double w, final boolean slim) {
final int subSi = sub.levelSize(rn, lv);
// new array, to be filled with the rectangles of the current level
rects[rn][lv] = new TreeRect[subSi];
double xx = rn * w * subSi + ds;
double ww = w;
for(int i = 0; i < subSi; ++i) {
if(slim) {
final double boxMiddle = xx + ww / 2f;
final byte[] b = getText(c, rn, sub.getPrePerIndex(rn, lv, i));
int o = calcOptimalRectWidth(g, b) + 10;
if(o < MIN_TXT_SPACE) o = MIN_TXT_SPACE;
if(w > o) {
xx = boxMiddle - o / 2d;
ww = o;
}
}
rects[rn][lv][i] = new TreeRect((int) xx + BORDER_PADDING, (int) ww
- BORDER_PADDING);
xx += w;
}
}
/**
* Returns TreeRects in given level.
* @param rn root number
* @param lv level
* @return TreeRect array
*/
TreeRect[] getTreeRectsPerLevel(final int rn, final int lv) {
return rects[rn][lv];
}
/**
* Returns TreeRect at given index position.
* @param rn root number
* @param lv level
* @param ix index
* @return tree rectangle
*/
TreeRect getTreeRectPerIndex(final int rn, final int lv, final int ix) {
return rects[rn][lv][ix];
}
/**
* Returns node text.
* @param c context
* @param rn root
* @param pre pre
* @return text
*/
static byte[] getText(final Context c, final int rn, final int pre) {
final Data d = c.data();
if(pre == c.current().list[rn]) return ViewData.path(d, pre);
return ViewData.content(d, pre, false);
}
/**
* Calculates optimal rectangle width.
* @param g the graphics reference
* @param b byte array
* @return optimal rectangle width
*/
private static int calcOptimalRectWidth(final Graphics g, final byte[] b) {
return BaseXLayout.width(g, b);
}
/**
* Returns true if big rectangle, false else.
* @param sub subtree
* @param rn root
* @param lv level
* @return boolean
*/
boolean bigRect(final TreeSubtree sub, final int rn, final int lv) {
return !(sub.levelSize(rn, lv) == rects[rn][lv].length);
}
/**
* Returns pre value for given x position of a big rectangle.
* @param sub subtree
* @param rn root
* @param lv level
* @param x x position
* @return pre value
*/
int getPrePerXPos(final TreeSubtree sub, final int rn, final int lv,
final int x) {
final TreeRect r = getTreeRectsPerLevel(rn, lv)[0];
final double ratio = (x - r.x) / (double) r.w;
final int idx = (int) (ratio * sub.levelSize(rn, lv));
return sub.getPrePerIndex(rn, lv, idx);
}
/**
* Uses binary search to find the rectangle with given pre value.
* @param sub subtree
* @param rn root number
* @param lv level
* @param pre the pre value to be found
* @return the rectangle containing the given pre value, {@code null} else
*/
TreeRect searchRect(final TreeSubtree sub, final int rn, final int lv,
final int pre) {
final int i = sub.searchPreArrayPos(rn, lv, pre);
return i < 0 ? null : bigRect(sub, rn, lv) ? rects[rn][lv][0]
: rects[rn][lv][i];
}
}