package org.basex.gui.layout;
import static org.basex.gui.GUIConstants.GRAY;
import static org.basex.gui.GUIConstants.LGRAY;
import static org.basex.gui.GUIConstants.WHITE;
import static org.basex.gui.GUIConstants.color;
import static org.basex.gui.GUIConstants.fontWidths;
import static org.basex.gui.layout.BaseXKeys.DECFONT;
import static org.basex.gui.layout.BaseXKeys.ENTER;
import static org.basex.gui.layout.BaseXKeys.ESCAPE;
import static org.basex.gui.layout.BaseXKeys.GOBACK;
import static org.basex.gui.layout.BaseXKeys.GOFORWARD;
import static org.basex.gui.layout.BaseXKeys.GOHOME;
import static org.basex.gui.layout.BaseXKeys.GOUP;
import static org.basex.gui.layout.BaseXKeys.INCFONT1;
import static org.basex.gui.layout.BaseXKeys.INCFONT2;
import static org.basex.gui.layout.BaseXKeys.INPUT1;
import static org.basex.gui.layout.BaseXKeys.INPUT2;
import static org.basex.gui.layout.BaseXKeys.NORMFONT;
import static org.basex.util.Token.chopNumber;
import static org.basex.util.Token.cl;
import static org.basex.util.Token.cp;
import static org.basex.util.Token.string;
import static org.basex.util.Token.token;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import org.basex.core.Prop;
import org.basex.gui.GUI;
import org.basex.gui.GUICommands;
import org.basex.gui.GUIProp;
import org.basex.gui.dialog.Dialog;
import org.basex.util.Util;
import org.basex.util.list.ObjList;
/**
* This class assembles layout and paint methods which are frequently
* used in the GUI.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class BaseXLayout {
/** Cached images. */
private static final HashMap<String, ImageIcon> IMAGES =
new HashMap<String, ImageIcon>();
/** Private constructor. */
private BaseXLayout() { }
/**
* Sets the help text for the specified component.
* @param cont input container
*/
static void focus(final Component cont) {
final GUI gui = gui(cont);
if(gui == null) return;
if(gui.gprop.is(GUIProp.MOUSEFOCUS)) cont.requestFocusInWindow();
}
/**
* Returns the GUI reference of the specified container.
* @param cont input container
* @return gui
*/
private static GUI gui(final Component cont) {
final Container c = cont.getParent();
return c == null || c instanceof GUI ? (GUI) c : gui(c);
}
/**
* Sets the component width, adopting the original component height.
* @param comp component
* @param w width
*/
public static void setWidth(final Component comp, final int w) {
comp.setPreferredSize(new Dimension(w, comp.getPreferredSize().height));
}
/**
* Sets the component height, adopting the original component width.
* @param comp component
* @param h height
*/
public static void setHeight(final Component comp, final int h) {
comp.setPreferredSize(new Dimension(comp.getPreferredSize().width, h));
}
/**
* Sets the component size.
* @param comp component
* @param w width
* @param h height
*/
static void setSize(final Component comp, final int w, final int h) {
comp.setPreferredSize(new Dimension(w, h));
}
/**
* Adds drag and drop functionality.
* @param comp component
* @param dnd drag and drop handler
*/
public static void addDrop(final JComponent comp, final DropHandler dnd) {
comp.setDropTarget(new DropTarget(comp,
DnDConstants.ACTION_COPY_OR_MOVE, null, true, null) {
@Override
public synchronized void drop(final DropTargetDropEvent dtde) {
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
final Transferable tr = dtde.getTransferable();
for(final Object o : contents(tr)) dnd.drop(o);
comp.requestFocusInWindow();
}
});
}
/**
* Returns the contents of the specified transferable.
* @param tr transferable
* @return contents
*/
@SuppressWarnings("unchecked")
public static ObjList<Object> contents(final Transferable tr) {
final ObjList<Object> list = new ObjList<Object>();
try {
if(tr.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
for(final File fl : (List<File>)
tr.getTransferData(DataFlavor.javaFileListFlavor)) list.add(fl);
} else if(tr.isDataFlavorSupported(DataFlavor.stringFlavor)) {
list.add(tr.getTransferData(DataFlavor.stringFlavor));
}
} catch(final Exception ex) {
Util.stack(ex);
}
return list;
}
/**
* Drag and drop handler.
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public interface DropHandler {
/**
* Drops a file.
* @param obj object to be dropped
*/
void drop(final Object obj);
}
/**
* Adds default interactions to the specified component.
* @param comp component
* @param win parent window
*/
static void addInteraction(final Component comp, final Window win) {
comp.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(final MouseEvent e) {
focus(comp);
}
});
if(win instanceof Dialog) {
// add default keys
final Dialog d = (Dialog) win;
comp.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(final KeyEvent e) {
final Object s = e.getSource();
if(s instanceof BaseXCombo && ((BaseXCombo) s).isPopupVisible())
return;
// process key events
if(ENTER.is(e)) {
if(!(s instanceof BaseXButton)) d.close();
} else if(ESCAPE.is(e)) {
d.cancel();
}
}
});
return;
}
if(!(win instanceof GUI)) {
Util.notexpected("Reference to main window required to add shortcuts.");
return;
}
final GUI gui = (GUI) win;
// add default keys for main window
comp.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(final KeyEvent e) {
// browse back/forward
if(gui.context.data() != null) {
if(GOBACK.is(e)) {
GUICommands.C_GOBACK.execute(gui);
} else if(GOFORWARD.is(e)) {
GUICommands.C_GOFORWARD.execute(gui);
} else if(GOUP.is(e)) {
GUICommands.C_GOUP.execute(gui);
} else if(GOHOME.is(e)) {
GUICommands.C_GOHOME.execute(gui);
}
}
// jump to input bar
if(INPUT1.is(e) || INPUT2.is(e)) {
gui.input.requestFocusInWindow();
}
// change font size
final int fs = gui.gprop.num(GUIProp.FONTSIZE);
int nfs = fs;
if(INCFONT1.is(e) || INCFONT2.is(e)) {
nfs = fs + 1;
} else if(DECFONT.is(e)) {
nfs = Math.max(1, fs - 1);
} else if(NORMFONT.is(e)) {
nfs = 12;
}
if(fs != nfs) {
gui.gprop.set(GUIProp.FONTSIZE, nfs);
gui.updateLayout();
}
}
});
}
/**
* Returns the specified image as icon.
* @param name name of icon
* @return icon
*/
public static ImageIcon icon(final String name) {
ImageIcon img = IMAGES.get(name);
if(img != null) return img;
img = new ImageIcon(image(name));
IMAGES.put(name, img);
return img;
}
/**
* Returns the specified image.
* @param name name of image
* @return image
*/
public static Image image(final String name) {
return Toolkit.getDefaultToolkit().getImage(imageURL(name));
}
/**
* Returns the image url.
* @param name name of image
* @return url
*/
private static URL imageURL(final String name) {
// changed Christoph Plutte
// final String path = "/img/" + name + ".png";
// final URL url = GUI.class.getResource(path);
final String path = Prop.HOME + "WORK" + "/img/" + name + ".png";
File f = new File(path);
URL url = null;
try
{
url = f.toURI().toURL();
}
catch (MalformedURLException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
if(url == null) Util.errln("Not found: " + path);
return url;
}
/**
* Returns the value of the specified pre value and attribute.
* @param val value to be evaluated
* @return value as string
*/
public static String value(final double val) {
return string(chopNumber(token(val)));
}
/**
* Fills the specified area with a color gradient.
* @param gg graphics reference
* @param c1 first color
* @param c2 second color
* @param xs horizontal start position
* @param ys vertical start position
* @param xe horizontal end position
* @param ye vertical end position
*/
public static void fill(final Graphics gg, final Color c1,
final Color c2, final int xs, final int ys, final int xe, final int ye) {
final int w = xe - xs;
final int h = ye - ys;
final int r = c1.getRed();
final int g = c1.getGreen();
final int b = c1.getBlue();
final float rf = (c2.getRed() - r) / (float) h;
final float gf = (c2.getGreen() - g) / (float) h;
final float bf = (c2.getBlue() - b) / (float) h;
int nr, ng, nb;
int cr = 0, cg = 0, cb = 0;
int hh = 0;
for(int y = 0; y < h; ++y) {
nr = r + (int) (rf * y);
ng = g + (int) (gf * y);
nb = b + (int) (bf * y);
if(nr != cr || ng != cg || nb != cb) {
gg.setColor(new Color(nr, ng, nb));
gg.fillRect(xs, ys + y - hh, w, hh);
hh = 0;
}
cr = nr;
cg = ng;
cb = nb;
++hh;
}
gg.fillRect(xs, ys + h - hh, w, hh);
}
/**
* Draws a colored cell.
* @param g graphics reference
* @param xs horizontal start position
* @param xe horizontal end position
* @param ys vertical start position
* @param ye vertical end position
* @param focus highlighting flag
*/
public static void drawCell(final Graphics g, final int xs,
final int xe, final int ys, final int ye, final boolean focus) {
g.setColor(GRAY);
g.drawRect(xs, ys, xe - xs - 1, ye - ys - 1);
g.setColor(Color.white);
g.drawRect(xs + 1, ys + 1, xe - xs - 3, ye - ys - 3);
fill(g, focus ? LGRAY : Color.white, LGRAY,
xs + 2, ys + 2, xe - 1, ye - 1);
}
/**
* Draws a centered string to the panel.
* @param g graphics reference
* @param text text to be painted
* @param w panel width
* @param y vertical position
*/
public static void drawCenter(final Graphics g, final String text,
final int w, final int y) {
g.drawString(text, (w - width(g, text)) / 2, y);
}
/**
* Draws a visualization tooltip.
* @param g graphics reference
* @param tt tooltip label
* @param x horizontal position
* @param y vertical position
* @param w width
* @param c color color depth
*/
public static void drawTooltip(final Graphics g, final String tt,
final int x, final int y, final int w, final int c) {
final int tw = width(g, tt);
final int th = g.getFontMetrics().getHeight();
final int xx = Math.min(w - tw - 8, x);
g.setColor(color(c));
g.fillRect(xx - 1, y - th, tw + 4, th);
g.setColor(WHITE);
g.drawString(tt, xx, y - 4);
}
/**
* Returns the width of the specified text.
* @param g graphics reference
* @param s string to be evaluated
* @return string width
*/
public static int width(final Graphics g, final String s) {
return g.getFontMetrics().stringWidth(s);
}
/**
* Draws the specified string.
*
* @param g graphics reference
* @param s text
* @param x x coordinate
* @param y y coordinate
* @param w width
* @param fs font size
*/
public static void chopString(final Graphics g, final byte[] s,
final int x, final int y, final int w, final int fs) {
if(w < 12) return;
final int[] cw = fontWidths(g.getFont());
int j = s.length;
int fw = 0;
int l = 0;
try {
for(int k = 0; k < j; k += l) {
final int ww = width(g, cw, cp(s, k));
if(fw + ww >= w - 4) {
j = Math.max(1, k - l);
if(k > 1) fw -= width(g, cw, cp(s, k - 1));
g.drawString("..", x + fw, y + fs);
break;
}
fw += ww;
l = cl(s, k);
}
} catch(final Exception ex) {
Util.debug(ex);
}
g.drawString(string(s, 0, j), x, y + fs);
}
/**
* Returns the width of the specified text.
* Cached font widths are used to speed up calculation.
* @param g graphics reference
* @param s string to be evaluated
* @return string width
*/
public static int width(final Graphics g, final byte[] s) {
final int[] cw = fontWidths(g.getFont());
final int l = s.length;
int fw = 0;
try {
// ignore faulty character sets
for(int k = 0; k < l; k += cl(s, k)) fw += width(g, cw, cp(s, k));
} catch(final Exception ex) {
Util.debug(ex);
}
return fw;
}
/**
* Returns the character width of the specified character.
* @param g graphics reference
* @param cw array with character widths
* @param c character
* @return character width
*/
public static int width(final Graphics g, final int[] cw, final int c) {
return c >= cw.length ? g.getFontMetrics().charWidth(c) : cw[c];
}
}