/*******************************************************************************
* Copyright (c) 2014 Mentor Graphics and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Mentor Graphics - initial API and implementation
*******************************************************************************/
package com.codesourcery.internal.installer.ui;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import com.codesourcery.internal.installer.ui.DetailTree.ItemVisitor;
/**
* Item for detail tree.
*/
public class DetailTreeItem {
/** No items constant */
private static final DetailTreeItem[] NO_ITEMS = new DetailTreeItem[0];
/**
* Item regions
*/
enum ItemRegion {
/** Expand area */
EXPAND,
/** Check-box area */
CHECKBOX,
/** Text area */
TEXT,
/** Description area */
DESCRIPTION
}
/** Style flags */
private int style;
/** Tree for item */
private DetailTree tree;
/** Parent item or <code>null</code> */
private DetailTreeItem parent;
/** Item text */
private String text;
/** Item description */
private String description;
/** Item data */
private Object data;
/** Item child items */
private DetailTreeItem[] children = NO_ITEMS;
/** Item area */
private Rectangle area;
/** Item regions */
private Rectangle[] regions = new Rectangle[ItemRegion.values().length];
/** <code>true</code> if item is expanded */
private boolean expanded = false;
/** <code>true</code> is item is checked */
private boolean checked;
/**
* Constructor
*
* @param tree Tree for item
* @param style SWT.CHECK for check-box item
*/
public DetailTreeItem(DetailTree tree, int style) {
this.tree = tree;
this.style = style;
this.parent = null;
tree.addItem(this);
}
/**
* Constructor
*
* @param item Parent item
* @param style SWT.CHECK for check-box item
*/
public DetailTreeItem(DetailTreeItem item, int style) {
this.tree = item.getTree();
this.style = style;
this.parent = item;
item.addChild(this);
// If parent is checked, this item is also checked
if (item.isChecked()) {
this.checked = true;
}
}
/**
* Returns the item style.
*
* @return Style
*/
public int getStyle() {
return style;
}
/**
* Returns the tree.
*
* @return Tree
*/
public DetailTree getTree() {
return tree;
}
/**
* Returns the parent item.
*
* @return Parent item or <code>null</code>
*/
public DetailTreeItem getParent() {
return parent;
}
/**
* Returns if the item has children.
*
* @return <code>true</code> if item has children
*/
public boolean hasChildren() {
return (children.length > 0);
}
/**
* Returns the item children.
*
* @return Children
*/
public DetailTreeItem[] getChildren() {
return children;
}
/**
* Adds a new child item.
*
* @param child Child item
*/
void addChild(DetailTreeItem child) {
DetailTreeItem[] newItems = new DetailTreeItem[children.length + 1];
System.arraycopy(children, 0, newItems, 0, children.length);
newItems[newItems.length - 1] = child;
children = newItems;
}
/**
* Checks an item and sends a notification.
*
* @param checked <code>true</code> to check, <code>false</code> to un-check
* @param notify <code>true</code> to send notification
*/
private void check(boolean checked, boolean notify) {
if (this.checked != checked) {
this.checked = checked;
getTree().redraw(this, ItemRegion.CHECKBOX);
if (notify) {
getTree().notifyCheckStateChanged(this);
}
}
}
/**
* Sets the item checked/un-checked.
*
* @param checked <code>true</code> if checked
*/
public void setChecked(final boolean checked) {
setChecked(checked, false);
}
/**
* Sets the item checked/un-checked.
*
* @param checked <code>true</code> if checked
* @param notify <code>true</code> to send notification
*/
void setChecked(final boolean checked, final boolean notify) {
if (isChecked() != checked) {
// Check item and all child items
getTree().visitItems(this, new ItemVisitor() {
@Override
public boolean visit(DetailTreeItem item) {
item.check(checked, notify);
return true;
}
});
// Checked
if (checked) {
// If all child items of parent item are checked,
// check the parent item
if (getParent() != null) {
boolean allChecked = true;
DetailTreeItem[] parentItems = getParent().getChildren();
for (DetailTreeItem parentItem : parentItems) {
if (((parentItem.getStyle() & SWT.CHECK) == SWT.CHECK) &&
!parentItem.isChecked()) {
allChecked = false;
break;
}
}
if (allChecked) {
getParent().check(true, notify);
getTree().redraw(getParent(), ItemRegion.CHECKBOX);
}
}
}
// Un-checked
else {
// Un-check all parent items
DetailTreeItem iter = this;
while (iter != null) {
iter.check(false, notify);
iter = iter.getParent();
}
}
}
}
/**
* Returns if the item is checked.
*
* @return <code>true</code> if checked
*/
public boolean isChecked() {
return checked;
}
/**
* Sets the item area.
*
* @param area Item area
*/
void setArea(Rectangle area) {
this.area = area;
}
/**
* Returns the item area.
*
* @return Item area
*/
Rectangle getArea() {
return area;
}
/**
* Returns the region that corresponds to mouse coordinates.
*
* @param x Horizontal coordinate
* @param y Vertical coordinate
* @return Item region or <code>null</code>
*/
ItemRegion hitTest(int x, int y) {
ItemRegion region = null;
for (int index = 0; index < regions.length; index ++) {
if ((regions[index] != null) && regions[index].contains(x, y)) {
region = ItemRegion.values()[index];
break;
}
}
return region;
}
/**
* Returns the area for a region.
*
* @param region Region
* @return Area or <code>null</code>
*/
Rectangle getRegion(ItemRegion region) {
return regions[region.ordinal()];
}
/**
* Clears all item regions.
*/
void clearRegions() {
for (int index = 0; index < regions.length; index ++) {
regions[index] = null;
}
}
/**
* Sets an item region.
*
* @param region Region
* @param area Region area
*/
void setRegion(ItemRegion region, Rectangle area) {
regions[region.ordinal()] = area;
}
/**
* Returns if the item is revealed (a child of expanded parents).
*
* @return <code>true</code> if item is revealed
*/
boolean isRevealed() {
DetailTreeItem parent = getParent();
while (parent != null) {
if (!parent.isExpanded())
return false;
parent = parent.getParent();
}
return true;
}
/**
* Expands the item if it is collapsed.
*/
public void expand() {
if (!isExpanded()) {
this.expanded = true;
getTree().recalcScrollBars();
getTree().redraw();
}
}
/**
* Expands the item and all its children.
*/
public void expandAll() {
expand();
DetailTreeItem[] children = getChildren();
for (DetailTreeItem child : children) {
child.expandAll();
}
}
/**
* Collapses the item if is expanded.
*/
public void collapse() {
if (isExpanded()) {
this.expanded = false;
getTree().recalcScrollBars();
getTree().redraw();
}
}
/**
* Collapses the item and all its children.
*/
public void collapseAll() {
collapse();
DetailTreeItem[] children = getChildren();
for (DetailTreeItem child : children) {
child.collapseAll();
}
}
/**
* Returns if the item is expanded.
*
* @return <code>true</code> if item is expanded
*/
public boolean isExpanded() {
return expanded;
}
/**
* Sets the item text.
*
* @param text Text
*/
public void setText(String text) {
this.text = text;
getTree().recalcScrollBars();
getTree().redraw(this, ItemRegion.TEXT);
}
/**
* Returns the item text.
*
* @return Item text
*/
public String getText() {
return text;
}
/**
* Sets the item description.
*
* @param description Description
*/
public void setDescription(String description) {
this.description = description;
getTree().recalcScrollBars();
getTree().redraw(this, ItemRegion.DESCRIPTION);
}
/**
* Returns the item description.
*
* @return Description
*/
public String getDescription() {
return description;
}
/**
* Sets data for the item.
*
* @param data Data or <code>null</code>
*/
public void setData(Object data) {
this.data = data;
}
/**
* Returns data for the item.
*
* @return Data or <code>null</code>
*/
public Object getData() {
return data;
}
}