/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.widgets;


import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;

/**
 * This class implements common behavior of TreeItem and TableItem.
 */
abstract class SelectableItem extends Item {
	protected static final int CHECKBOX_PADDING = 1;	// Space behind check box, before item image or text
		
	private SelectableItemWidget parent;				// parent widget of the receiver
	private boolean isSelected = false;					// item selection state
	private boolean isChecked = false;					// item checked state. Can be one of checked and unchecked
	private boolean isGrayed = false;					// item grayed state. The gray state is combined with the 
														// checked state to create gray checked and gray unchecked.
/**
 * Create a new instance of the receiver.
 * @param parent - widget the receiver is created in 
 * @param style - widget style. see Widget class for details
 */
SelectableItem(SelectableItemWidget parent, int style) {
	super(parent, style);
	setParent(parent);	
}

void clear() {
	isChecked = isGrayed = false;
}
public void dispose() {
	if (isDisposed()) return;
	super.dispose();
	doDispose();
}
void doDispose() {
	setParent(null);
}
/**
 * Draw the check box of the receiver at 'position' using 'gc'.
 * @param gc - GC to draw on. 
 * @param destinationPosition - position on the GC to draw at.
 * @return Answer the position where drawing stopped.
 */
Point drawCheckbox(GC gc, Point position) {
	SelectableItemWidget parent = getSelectableParent();
	Image image;
	Point imageExtent;
	Rectangle imageBounds;
	int imageOffset;
	int xInset;
	int yInset;

	if (getGrayed() == true) {
		image = parent.getGrayUncheckedImage();
	}
	else {
		image = parent.getUncheckedImage();
	}
	if (image != null) {
		imageExtent = parent.getCheckBoxExtent();
		imageOffset = (parent.getItemHeight() - imageExtent.y) / 2;
		gc.drawImage(image, position.x, position.y + imageOffset);
		if (getChecked() == true) {
			image = parent.getCheckMarkImage();
			imageBounds = image.getBounds();
			xInset = (imageExtent.x - imageBounds.width) / 2;
			yInset = (imageExtent.y - imageBounds.height) / 2;
			gc.drawImage(image, position.x + xInset, position.y + imageOffset + yInset);
		}
		position.x += imageExtent.x;
	}
	position.x += CHECKBOX_PADDING;									// leave extra space behind check box	
	return position;
}
void drawInsertMark(GC gc, Point position) {
	SelectableItemWidget parent = getSelectableParent();
	Point selectionExtent = getSelectionExtent();
	final int markerWidth = getInsertMarkWidth();
	int insertMarkYOffset = 0;

	if (selectionExtent == null) {
		return;
	}
	if (parent.isInsertAfter()) {
		insertMarkYOffset = selectionExtent.y - markerWidth;
	}
	gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
	gc.fillRectangle(position.x, position.y + insertMarkYOffset, selectionExtent.x, markerWidth);
	gc.setBackground(parent.getBackground());
}
/**
 * Answer the bounding rectangle of the item check box.
 * All points within this rectangle hit the check box.
 */
Rectangle getCheckboxBounds() {
	SelectableItemWidget parent = getSelectableParent();
	Point checkBoxExtent;
	int redrawPosition;
	Rectangle checkboxBounds = new Rectangle(0, 0, 0, 0);

	if (isCheckable() == true) {
		checkboxBounds.x = getCheckboxXPosition();
		redrawPosition = parent.getRedrawY(this);
		if (redrawPosition != -1) {
			checkboxBounds.y = redrawPosition;
		}
		checkBoxExtent = parent.getCheckBoxExtent();
		checkboxBounds.width = checkBoxExtent.x;
		checkboxBounds.height = checkBoxExtent.y;
		checkboxBounds.y += (parent.getItemHeight() - checkBoxExtent.y) / 2;
	}
	return checkboxBounds;
}
/**
 * Answer the x position of the item check box
 */
abstract int getCheckboxXPosition();
/**
 * Return whether or not the receiver is checked.
 * Always return false if the parent of the receiver does not
 * have the CHECK style.
 */
public boolean getChecked() {
	checkWidget();
	boolean checked = false;
	
	if (isCheckable() == true) {
		checked = isChecked;
	}
	return checked;
}

public Font getFont() {
	return parent.getFont();
}

/**
 * Gets the grayed state.
 * <p>
 * @return the item grayed state.
 *
 * @exception SWTError <ul>
 *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 *	</ul>
 */
public boolean getGrayed () {
	checkWidget();
	boolean grayed = false;
	
	if (isCheckable() == true) {
		grayed = isGrayed;
	}
	return grayed;
}
/**
 * Return the width in pixels of the line drawn to indicate the 
 * drop insert position during a drag and drop operation.
 */
int getInsertMarkWidth() {
	return 2;
}
/**
 * Answer the parent widget of the receiver.
 */
SelectableItemWidget getSelectableParent() {
	return parent;
}
/**
 * Answer the background color to use for drawing the 
 * selection rectangle.
 */
Color getSelectionBackgroundColor() {
	return display.getSystemColor(SWT.COLOR_LIST_SELECTION);
}
/**
 * Return the size of the rectangle drawn to indicate the
 * selected state of the receiver.
 */
abstract Point getSelectionExtent();
/** 
 * Answer the foreground color to use for drawing the 
 * selection rectangle.
 */
Color getSelectionForegroundColor() {
	return display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
}
/**
 * Return the x position of the selection rectangle
 */
abstract int getSelectionX();
/**
 * Answer whether 'posiiton' is inside the item check box.
 * @return
 *	true - item check box hit
 *	false - item check box not hit
 */
boolean isCheckHit(Point position) {
	boolean isCheckHit = false;
	
	if (isCheckable() == true) {
		isCheckHit = getCheckboxBounds().contains(position);
	}
	return isCheckHit;
}
/**
 * Return whether or not the receiver has a check box and can 
 * be checked.
 */
boolean isCheckable() {
	return (getSelectableParent().getStyle() & SWT.CHECK) != 0;
}
/**
 * Answer whether the receiver is selected.
 * @return 
 *	true - the receiver is selected
 * 	false - the receiver is not selected
 */
boolean isSelected() {
	return isSelected;
}
/**
 * Redraw the insert mark
 * @param yPosition - y position in the receiver's client area 
 *	where the item should be drawn.
 */
void redrawInsertMark(int yPosition) {
	SelectableItemWidget parent = getSelectableParent();
	Point selectionExtent = getSelectionExtent();
	int redrawHeight = getInsertMarkWidth();

	if (selectionExtent != null) {
		parent.redraw(getSelectionX(), yPosition, selectionExtent.x, redrawHeight, false);
		parent.redraw(getSelectionX(), yPosition + selectionExtent.y - redrawHeight, selectionExtent.x, redrawHeight, false);
	}	
}
/**
 * Redraw the selection
 * @param yPosition - y position in the receiver's client area 
 *	where the item should be drawn.
 */
void redrawSelection(int yPosition) {
	SelectableItemWidget parent = getSelectableParent();
	Point selectionExtent = getSelectionExtent();

	if (selectionExtent != null) {
		parent.redraw(getSelectionX(), yPosition, selectionExtent.x, selectionExtent.y, false);
	}
}
/**
 * Set the checked state to 'checked' if the parent of the 
 * receiver has the CHECK style.
 */
public void setChecked(boolean checked) {
	checkWidget();
	SelectableItemWidget parent = getSelectableParent();
	Rectangle redrawRectangle = getCheckboxBounds();

	if (isCheckable() == true && isChecked != checked) {
		isChecked = checked;
		parent.redraw(
			redrawRectangle.x, redrawRectangle.y, 
			redrawRectangle.width, redrawRectangle.height, false);
	}	
}

/**
 * Sets the grayed state.
 * <p>
 * @param grayed the new grayed state.
 *
 * @exception SWTError <ul>
 *		<li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
 *		<li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
 *	</ul>
 */
public void setGrayed (boolean grayed) {
	checkWidget();
	SelectableItemWidget parent = getSelectableParent();
	Rectangle redrawRectangle = getCheckboxBounds();

	if (isCheckable() == true && isGrayed != grayed) {
		isGrayed = grayed;
		parent.redraw(
			redrawRectangle.x, redrawRectangle.y, 
			redrawRectangle.width, redrawRectangle.height, false);
	}	
}

/**
 * Set the receiver's parent widget to 'parent'.
 */
void setParent(SelectableItemWidget parent) {
	this.parent = parent;
}
/**
 * Set whether the receiver is selected.
 * @param selected - true=the receiver is selected
 * 	false=the receiver is not selected
 */
void setSelected(boolean selected) {
	isSelected = selected;
}
}
