// Copyright 1996, Marimba Inc. All Rights Reserved.


// @(#)Dash.java, 1.11, 11/09/96





package marimba.gui;





import java.util.Hashtable;


import java.awt.*;


import java.awt.image.MemoryImageSource;





/**


 * A helper class for dashing horizontal or vertical lines


 * of varying thicknesses. The dashes are prerendered into


 * an image before they are drawn to the screen.


 *


 * @author	Arthur van Hoff


 * @version 	1.11, 11/09/96


 */


public final class Dash {


    static Hashtable hash;


    static Dash focus;





    int linewidth;


    int dashlen;


    int imglen;


    Color c1;


    Color c2;





    Image hDash;


    Image vDash;





    /**


     * Create a new dask of a given length, width and color combination.


     */


    public Dash(int dashlen, int linewidth, Color c1, Color c2) {


	dashlen &= 0x3E;


	linewidth &= 0x7;


	this.dashlen = dashlen;


	this.linewidth = linewidth;


	this.imglen = Math.max(dashlen, (200 / dashlen) * dashlen);


	this.c1 = c1;


	this.c2 = c2;


    }


    


    synchronized void init() {


	Toolkit tk = Toolkit.getDefaultToolkit();





	int col1 = (c1 == null) ? 0 : c1.getRGB();


	int col2 = (c2 == null) ? 0 : c2.getRGB();





	int pix[] = new int[imglen * linewidth];


	for (int n = (imglen * linewidth) / dashlen, off = 0 ; n-- > 0 ;) {


	    for (int i = dashlen/2 ; i-- > 0 ; ) {


		pix[off++] = col1;


	    }


	    for (int i = dashlen/2 ; i-- > 0 ; ) {


		pix[off++] = col2;


	    }


	}


	hDash = tk.createImage(new MemoryImageSource(imglen, linewidth, pix, 0, imglen));


	tk.prepareImage(hDash, -1, -1, null);





	pix = new int[imglen * linewidth];


	for (int n = imglen / dashlen, off = 0 ; n-- > 0 ; ) {


	    for (int i = (linewidth * dashlen) / 2 ; i-- > 0 ; ) {


		pix[off++] = col1;


	    }


	    for (int i = (linewidth * dashlen) / 2 ; i-- > 0 ; ) {


		pix[off++] = col2;


	    }


	}


	vDash = tk.createImage(new MemoryImageSource(linewidth, imglen, pix, 0, linewidth));


	tk.prepareImage(vDash, -1, -1, null);


    }





    void dashHorz(Graphics g, int x, int y, int w, int d) {


	for (x += d, w -= d ; x < w ; x += imglen) {


	    g.drawImage(hDash, x, y, null);


	}


    }





    void dashVert(Graphics g, int x, int y, int h, int d) {


	for (y += d, h -= d ; y < h ; y += imglen) {


	    g.drawImage(vDash, x, y, null);


	}


    }





    /**


     * Dash a line between two points. Only horizontal and vertical


     * lines are supported.


     */


    public void dashLine(Graphics g, int x1, int y1, int x2, int y2) {


	int w = x2 - x1;


	int h = y2 - y1;





	if (hDash == null) {


	    init();


	}


	


	if (w > 0) {


	    Graphics gc = g.create(x1, y1, w, linewidth);


	    try {


		dashHorz(gc, 0, 0, w, 0);


	    } finally {


		gc.dispose();


	    }


	} else {


	    Graphics gc = g.create(x1, y1, linewidth, h);


	    try {


		dashVert(gc, 0, 0, h, 0);


	    } finally {


		gc.dispose();


	    }


	}


    }





    /**


     * Dash a rectangle. The dashes are drawn inside the coordinates.


     */


    public void dashRect(Graphics g, int x, int y, int w, int h) {


	if (hDash == null) {


	    init();


	}





	Graphics gc = g.create(x, y, w, h);


	try {


	    dashHorz(gc, 0, 0, w, -dashlen/2);


	    int z = h - linewidth*2;


	    z = ((z / dashlen) * dashlen) - z;


	    dashHorz(gc, 0, h - linewidth, w, z);


	    gc.clipRect(0, linewidth, w, h - linewidth*2);


	    dashVert(gc, 0, linewidth, h - linewidth*2, 0);


	    z = ((w / dashlen) * dashlen) - w;


	    dashVert(gc, w - linewidth, linewidth, h - linewidth*2, z - dashlen/2);


	} finally {


	    gc.dispose();


	}


    }





    /**


     * Hash


     */


    public int hashCode() {


	int hash = (linewidth * 7) ^ (dashlen * 63);


	if (c1 != null) {


	    hash ^= c1.hashCode() * 13;


	}


	if (c2 != null) {


	    hash ^= c2.hashCode() * 47;


	}


	return hash;


    }





    /**


     * Equality


     */


    public boolean equals(Object obj) {


	if (obj instanceof Dash) {


	    Dash d = (Dash)obj;


	    if ((linewidth == d.linewidth) && (dashlen == d.dashlen) && 


		((c1 == d.c1) || ((c1 != null) && c1.equals(d.c1))) &&


		((c2 == d.c2) || ((c2 != null) && c2.equals(d.c2)))) {


		return true;


	    }


	}


	return false;


    }





    /**


     * Debugging.


     */


    public String toString() {


	return getClass().getName() + "[" + dashlen +"x" + linewidth + "," + c1 + "," + c2 + "]";


    }





    /**


     * Get a dash from the cache.


     */


    public static synchronized Dash getDash(int dashlen, int linewidth, Color c1, Color c2) {


	if (hash == null) {


	    hash = new Hashtable();


	}





	Dash d = new Dash(dashlen, linewidth, c1, c2);


	Dash dash = (Dash)hash.get(d);


	if (dash == null) {


	    hash.put(d, d);


	    return d;


	}


	return dash;


    }





    /**


     * Flush the dash cache.


     */


    public synchronized void flush() {


	hash = null;


	focus = null;


    }





    /**


     * Draw a dashed focus rectangle to show input focus.


     */


    public static synchronized void drawFocusRect(Graphics g, int x, int y, int width, int height) {


	if ((focus == null) || !focus.c2.equals(g.getColor())) {


	    focus = Dash.getDash(2, 1, null, g.getColor());


	}


	focus.dashRect(g, x, y, width, height);


    }





    /**


     * Draw a dashed focus line 


     */


    public static synchronized void drawFocusLine(Graphics g, int x1, int y1, int x2, int y2) {


	if ((focus == null) || !focus.c2.equals(g.getColor())) {


	    focus = Dash.getDash(2, 1, null, g.getColor());


	}


	focus.dashLine(g, x1, y1, x2, y2);


    }


}


