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


// @(#)SpinBoxTextWidget.java, 1.12, 12/23/96





package marimba.gui;





import java.awt.*;


import marimba.persist.*;





/**


 * A text widget with an up and down arrow for switching values.


 *


 * @author	Klaas Waslander


 * @version 	1.12, 12/23/96


 */


public class SpinBoxTextWidget extends TextBoxWidget {


    /**


     * The button that is being pressed. Target can be none, up or down.


     */


    public int  target = NONE;





    /**


     * Indicates whether one of the buttons is lowered.


     */


    public boolean  down = false;





    /**


     * A SpinBoxTextWidget can hide the background from the buttons


     * by filling the buttons with the background color.


     * @see #getFillButtons


     * @see #setFillButtons


     */


    public boolean fillButtons;








    /**


     * Constructor.


     */


    public SpinBoxTextWidget() {


	style = BOXED;


    }





    /**


     * Get the properties of the dropdown textbox.


     */


    public void getProperties(PropertyList list) {


	super.getProperties(list);


	list.setBoolean("fillButtons", fillButtons, false);


    }





    /**


     * Set the properties of the dropdown textbox.


     */


    public void setProperties(PropertyList list) {


	super.setProperties(list);


	fillButtons = list.getBoolean("fillButtons", false);


	transparent = !(style == FILLED && fillButtons);


    }





    /**


     * Check if the buttons of the spinboxtextwidget are filled.


     * @see #fillButtons


     */


    public boolean getFillButtons() {


	return fillButtons;


    }





    /**


     * Let the buttons of the spinboxtextwidget fill itself or not.


     * @see #fillButtons


     */


    public void setFillButtons(boolean fillButtons) {


	if (this.fillButtons != fillButtons) {


	    this.fillButtons = fillButtons;


	    transparent = !(fillButtons && style == FILLED);


	    repaint();


	}


    }





    /**


     * Set the style of this widget the same as in the


     * super class, but adjust the transparent boolean.


     * @see #style


     */


    public void setStyle(int style) {


	super.setStyle(style);


	transparent = !(fillButtons && style == FILLED);


    }





    /**


     * Insert a character; only integers are allowed!


     * It will do nothing if the length of the text has reached


     * the length of the maxValue of SpinBox.


     */


    public void insert(char ch) {


	String  maxValString = String.valueOf(getOwner().getMaxValue());


	if (getText().length() >= maxValString.length()) {


	    return;


	}


	try {


	    char newtext[] = new char[len+1];


	    System.arraycopy(text, 0, newtext, 0, len);


	    newtext[len] = ch;


	    int  tst = Integer.parseInt(new String(newtext));


	} catch (NumberFormatException e) {


	    return;


	}


	super.insert(ch);


	getOwner().postEvent(new Event(this, Event.KEY_PRESS, null));


    }





    /**


     * Right margin.


     */


    public int rightMargin() {


	return getButtonWidth() + 2;


    }





    /**


     * Get the width of the buttons.


     */


    public int getButtonWidth() {


	return getButtonHeight() * 2;


    }





    /**


     * Get the height of the buttons.


     */


    public int getButtonHeight() {


	return (height-6) / 2;


    }





    /**


     * Get the owner; the SpinTextBoxWidget gives events to the owners handleEvent.


     * The owner is always a SpinBoxWidget.


     */


    public SpinBoxWidget getOwner() {


	return (SpinBoxWidget) parent;


    }





    /**


     * Which button is located under a given (x,y) coordinate.


     * @return UP, DOWN or NONE


     */


    public int findButton(int x, int y) {


	int  buttonHeight = getButtonHeight();


	int  buttonWidth = getButtonWidth();


	int  buttonX2 = width - 2;


	int  buttonX1 = buttonX2 - buttonWidth;





	if (x >= buttonX1 && x <= buttonX2) {


	    if (y >= 2 && y <= buttonHeight+2) {


		return UP;


	    }


	    if (y >= height-2-buttonHeight && y <= height-2) {


		return DOWN;


	    }


	}


	return NONE;


    }





    /**


     * Paint the background and the two buttons.


     */


    public void paintBackground(Graphics g) {


	super.paintBackground(g);





	// some calculations


	int  buttonHeight = getButtonHeight();


	int  buttonWidth = getButtonWidth();


	int  buttonX = width - 3 - buttonWidth;


	int  buttonY = 2;


	


	// draw the two buttons


	int  mode1 = (target==UP && down) ? LOWERED : RAISED;


	int  mode2 = (target==DOWN && down) ? LOWERED : RAISED;


	paintButton(g, buttonX, buttonY, buttonWidth, buttonHeight, UP, mode1);


	paintButton(g, buttonX, buttonY+buttonHeight+1, buttonWidth, buttonHeight, DOWN, mode2);


    }





    /**


     * Paint a button raised or lowered and with an up or down arrow.


     * Constants defined in WidgetConstants are used for that.


     */


    public void paintButton(Graphics g, int x, int y, int width, int height, int dir, int mode) {


	Color  darker = background.darker();;


	Color  brighter = background.brighter();





	if (fillButtons) {


	    g.setColor(background);


	    g.fillRect(x, y, width, height);


	}





	g.setColor((mode==RAISED) ? background : darker);


	g.drawLine(x, y, x+width-1, y);


	g.drawLine(x, y+1, x, y+height-1);





	g.setColor((mode==RAISED) ? brighter : Color.black);


	g.drawLine(x+1, y+1, x+width-2, y+1);


	g.drawLine(x+1, y+2, x+1, y+height-2);





	g.setColor((mode==RAISED) ? darker : background);


	g.drawLine(x+1, y+height-1, x+width-1, y+height-1);


	g.drawLine(x+width-1, y+height-2, x+width-1, y+1);


	


	g.setColor((mode==RAISED) ? Color.black : brighter);


	g.drawLine(x+width, y, x+width, y+height);


	g.drawLine(x, y+height, x+width-1, y+height);





	g.setColor(foreground);


	int  arrowHeight = height - 5;


	int  arrowX = x + 2 + (width - 3) / 2;


	int  arrowY = y + 3;


	if (mode != RAISED) {


	    arrowX += 1;


	    arrowY += 1;


	}


	if (dir == UP) {


	    for (int index = 0; index < arrowHeight; index++) {


		g.drawLine(arrowX, arrowY, arrowX + 2*index, arrowY);


		arrowX -= 1;


		arrowY += 1;


	    }


	} else if (dir == DOWN) {


	    arrowY += arrowHeight-1;


	    for (int index = 0; index < arrowHeight; index++) {


		g.drawLine(arrowX, arrowY, arrowX + 2*index, arrowY);


		arrowX -= 1;


		arrowY -= 1;


	    }


	}


    }





    /**


     * Event is not in text area when it is on button.


     */


    protected boolean eventInText(Event evt) {


	return super.eventInText(evt) && evt.x < width - getButtonWidth();


    }





    /**


     * Handle user events.


     */


    public boolean handleEvent(Event evt) {


	if (disabled) {


	    return true;


	}


	SpinBoxWidget  owner = getOwner();


	switch (evt.id) {


	    case Event.LOST_FOCUS:


		owner.postEvent(new Event(this, Event.LOST_FOCUS, null));


		break;





	    case Event.MOUSE_DOWN:


		target = findButton(evt.x, evt.y);


		if (target != NONE) {


		    int  newVal = Integer.parseInt(getText());


		    if (newVal != owner.value) {


			owner.setValue(newVal);


		    }


		    down = true;


		    repaint();


		    action();


		    return false;


		}


		break;





	    case Event.MOUSE_DRAG:


		int which = findButton(evt.x, evt.y);


		if (which==target) {


		    if (which!=NONE && !down) {


			down = true;


			repaint();


			action();


			return true;


		    }


		} else if (down) {


		    down = false;


		    repaint();


		    owner.postEvent(new Event(this, Event.MOUSE_UP, null));


		    return true;


		}


		break;





	    case Event.MOUSE_UP:


		if (down) {


		    down = false;


		    target = NONE;


		    repaint();


		    owner.postEvent(new Event(this, Event.MOUSE_UP, null));


		    return true;


		}


		// propagate MOUSE_UP to SpinBoxWidget with proper x and y


		owner.postEvent(new Event(this, evt.when, Event.MOUSE_UP, evt.x, evt.y, evt.key, evt.modifiers, null));


		break;





	    case Event.KEY_ACTION:


		switch (evt.key) {


		    case Event.UP:


			if (!down) {


			    target = UP;


			    down = true;


			    repaint();


			    action();


			}


			return true;





		    case Event.DOWN:


			if (!down) {


			    target = DOWN;


			    down = true;


			    repaint();


			    action();


			}


			return true;


		}


		break;





	    case Event.KEY_ACTION_RELEASE:


		switch (evt.key) {


		    case Event.UP:


		    case Event.DOWN:


			if (down) {


			    target = NONE;


			    down = false;


			    repaint();


			    owner.postEvent(new Event(this, Event.MOUSE_UP, null));


			}


			return true;


		}


		break;


	}


	return super.handleEvent(evt);


    }





    /**


     * The user tried to change the value.


     */


    public void action() {


	int  eventID;


	if (target == UP) {


	    eventID = Event.SCROLL_LINE_UP;


	} else if (target == DOWN) {


	    eventID = Event.SCROLL_LINE_DOWN;


	} else {


	    // no up or down arrow targeted, so it's time for the default action


	    getOwner().setValue(Integer.parseInt(getText()));


	    super.action();


	    return;


	}


	getOwner().postEvent(new Event(this, eventID, null));


    }


}


