/*
* File : AbstractBox.java
* Created : 12-dec-2000 10:55
* By : fbusquets
*
* JClic - Authoring and playing system for educational activities
*
* Copyright (C) 2000 - 2005 Francesc Busquets & Departament
* d'Educacio de la Generalitat de Catalunya
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details (see the LICENSE file).
*/
package edu.xtec.jclic.boxes;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.ImageObserver;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.text.JTextComponent;
/**
* This abstract class is the base for most graphic components of JClic. It describes
* an {@link java.awt.geom.Area} (a rectangle by default) with some special properties
* that determine how it must be drawn on screen. Some types of boxes can act as
* containers for other boxes, establishing a hierarchy of dependences.
* Box objects are always placed into a {@link javax.swing.JComponent}.
* @author Francesc Busquets (fbusquets@xtec.cat)
* @version 13.08.28
*/
public abstract class AbstractBox extends Rectangle2D.Double implements Cloneable {
private AbstractBox parent;
private JComponent container;
private BoxBase boxBase;
protected boolean border;
protected Shape shape;
protected boolean specialShape;
private boolean visible;
private boolean temporaryHidden;
private boolean inactive;
private boolean inverted;
private boolean alternative;
private boolean marked;
private boolean focused;
private Component hostedComponent;
public AbstractBox(AbstractBox parent, JComponent container, BoxBase boxBase) {
this.parent=parent;
this.container=container;
shape=this;
specialShape=false;
this.boxBase=boxBase;
visible=true;
}
@Override
public Object clone(){
AbstractBox bx=(AbstractBox)super.clone();
if(specialShape) bx.shape=new Area(shape);
else bx.shape=bx;
if(hostedComponent!=null){
if(hostedComponent instanceof JLabel){
JLabel lb=(JLabel)hostedComponent;
bx.hostedComponent=new JLabel(lb.getText(), lb.getIcon(),
lb.getHorizontalAlignment());
JComponent jc=bx.getContainerResolve();
if(jc!=null)
jc.add(hostedComponent);
}
else bx.hostedComponent=null;
}
return bx;
}
public void setParent(AbstractBox parent){
this.parent=parent;
}
public AbstractBox getParent(){
return parent;
}
public void end(){
setHostedComponent(null);
}
@Override
public void finalize() throws Throwable{
end();
super.finalize();
}
public void setContainer(JComponent newContainer){
container=newContainer;
if(hostedComponent!=null){
if(hostedComponent.getParent()!=null){
hostedComponent.getParent().remove(hostedComponent);
}
if(container!=null)
container.add(hostedComponent);
}
}
public JComponent getContainerX(){
return container;
}
public JComponent getContainerResolve(){
AbstractBox ab=this;
while(ab.container==null && ab.parent!=null)
ab=ab.parent;
return ab.container;
}
public void setBoxBase(BoxBase boxBase){
this.boxBase=boxBase;
//if(bb==null) bb=BoxBase.DefaultBoxBase;
repaint();
setHostedComponentColors();
setHostedComponentBorder();
}
public BoxBase getBoxBaseResolve(){
AbstractBox ab=this;
while(ab.boxBase==null && ab.parent!=null)
ab=ab.parent;
return ab.boxBase==null ? BoxBase.DEFAULT_BOX_BASE : ab.boxBase;
}
public BoxBase getBoxBaseX(){
return boxBase;
}
public void setHostedComponent(Component jc){
if(hostedComponent!=null){
if(hostedComponent.getParent()!=null){
hostedComponent.getParent().remove(hostedComponent);
}
}
hostedComponent=jc;
if(hostedComponent!=null){
hostedComponent.setVisible(false);
JComponent cmp=getContainerResolve();
if(cmp!=null)
cmp.add(jc);
setHostedComponentColors();
setHostedComponentBorder();
setHostedComponentBounds();
hostedComponent.setVisible(visible);
}
}
public Component getHostedComponent(){
return hostedComponent;
}
// canviar nom!!!
public void setShape(Shape setSh){
shape=setSh;
specialShape=true;
repaint();
super.setRect(shape.getBounds2D());
setHostedComponentBounds();
repaint();
}
public Shape getShape(){
return shape;
}
@Override
public boolean contains(Point2D p){
return shape==this ? super.contains(p) : shape.contains(p);
}
public boolean update(Graphics2D g2, Rectangle dirtyRegion, ImageObserver io){
if(hostedComponent!=null)
return false;
if(isEmpty() || !isVisible() || isTemporaryHidden())
return false;
if(dirtyRegion!=null && !shape.intersects(dirtyRegion))
return false;
Shape saveClip=new Area(g2.getClip());
Area clip=new Area(saveClip);
clip.intersect(new Area(shape));
g2.setClip(clip);
BoxBase bb=getBoxBaseResolve();
if(!bb.transparent){
if(bb.bgGradient==null || bb.bgGradient.hasTransparency()){
g2.setColor(inactive
? bb.inactiveColor
: inverted ? bb.textColor : bb.backColor);
g2.fill(shape);
g2.setColor(Color.black);
}
if(bb.bgGradient!=null)
bb.bgGradient.paint(g2, shape);
}
updateContent(g2, dirtyRegion, io);
g2.setClip(saveClip);
drawBorder(g2);
return true;
}
public abstract boolean updateContent(Graphics2D g2, Rectangle dirtyRegion, ImageObserver io);
protected void drawBorder(Graphics2D g2){
if(border || marked || focused){
BoxBase bb=getBoxBaseResolve();
g2.setColor(bb.borderColor);
g2.setStroke((marked || focused) ? bb.getMarker() : bb.getBorder());
if(marked || focused) g2.setXORMode(Color.white);
g2.draw(shape);
if(marked || focused) g2.setPaintMode();
g2.setColor(Color.black);
g2.setStroke(BoxBase.DEFAULT_STROKE);
}
}
public Rectangle getBorderBounds(){
if(!border && !marked && !focused) return getBounds();
BoxBase bb=getBoxBaseResolve();
Stroke strk=(marked || focused) ? bb.getMarker() : bb.getBorder();
return strk.createStrokedShape(shape).getBounds();
}
public boolean hasBorder() {return border;}
public void setBorder(boolean newVal){
if(!newVal) repaint();
border=newVal;
setHostedComponentBorder();
if(newVal) repaint();
}
public boolean isVisible() {return visible;}
public void setVisible(boolean newVal) {
visible=newVal;
if(hostedComponent!=null)
hostedComponent.setVisible(newVal);
repaint();
}
public boolean isTemporaryHidden() {return temporaryHidden;}
public void setTemporaryHidden(boolean newVal) {
temporaryHidden=newVal;
//repaint();
}
public boolean isInactive() {return inactive;}
public void setInactive(boolean newVal) {
inactive=newVal;
checkHostedComponent();
setHostedComponentColors();
repaint();
}
public boolean isInverted() {return inverted;}
public void setInverted(boolean newVal) {
inverted=newVal;
checkHostedComponent();
setHostedComponentColors();
repaint();
}
public boolean isMarked() {
return marked;
}
public void setMarked(boolean newVal) {
if(!newVal) repaint();
marked=newVal;
checkHostedComponent();
setHostedComponentColors();
if(newVal) repaint();
}
public boolean isFocused() {
return focused;
}
public void setFocused(boolean newVal) {
if(!newVal) repaint();
focused=newVal;
checkHostedComponent();
setHostedComponentColors();
if(newVal) repaint();
}
public boolean isAlternative() {
return alternative;
}
public void setAlternative(boolean newVal) {
alternative=newVal;
checkHostedComponent();
setHostedComponentColors();
repaint();
}
private void setHostedComponentColors(){
if(hostedComponent!=null){
BoxBase bb=getBoxBaseResolve();
hostedComponent.setFont(bb.getFont());
hostedComponent.setBackground(inactive ? bb.inactiveColor
: inverted ? bb.textColor
: bb.backColor);
hostedComponent.setForeground(inverted ? bb.backColor
: alternative ? bb.alternativeColor
: bb.textColor);
if(hostedComponent instanceof JComponent){
((JComponent)hostedComponent).setOpaque(true);
if(hostedComponent instanceof JTextComponent)
((JTextComponent)hostedComponent).setCaretColor(bb.textColor);
}
}
}
private void setHostedComponentBorder(){
if(hostedComponent!=null && hostedComponent instanceof JComponent){
((JComponent)hostedComponent).setBorder(
border
? BorderFactory.createLineBorder(getBoxBaseResolve().borderColor, 1)
: BorderFactory.createEmptyBorder());
}
}
private void setHostedComponentBounds(){
if(hostedComponent!=null){
Rectangle r=getBounds();
hostedComponent.setBounds(r);
if(hostedComponent instanceof JComponent)
((JComponent)hostedComponent).setPreferredSize(r.getSize());
}
}
public void repaint(){
JComponent jc=getContainerResolve();
if(jc!=null)
jc.repaint(getBorderBounds());
if(hostedComponent!=null)
hostedComponent.repaint();
}
public void setBounds(Rectangle2D r){
if(r.equals((Rectangle2D)this)) return;
if(specialShape){
AffineTransform tx = null;
if(getWidth()!=r.getWidth() || getHeight()!=r.getHeight()){
tx=AffineTransform.getTranslateInstance(-getX(), -getY());
shape=tx.createTransformedShape(shape);
tx=AffineTransform.getScaleInstance(r.getWidth()/getWidth(), r.getHeight()/getHeight());
shape=tx.createTransformedShape(shape);
tx=AffineTransform.getTranslateInstance(getX(), getY());
shape=tx.createTransformedShape(shape);
}
if(getX()!=r.getX() || getY()!=r.getY()){
tx=AffineTransform.getTranslateInstance(r.getX()-getX(), r.getY()-getY());
shape=tx.createTransformedShape(shape);
}
if(tx!=null){
setShape(shape);
}
}
else{
repaint();
setRect(r);
repaint();
}
setHostedComponentBounds();
}
public void setBounds(double newX, double newY, double newWidth, double newHeight){
setBounds(new Double(newX, newY, newWidth, newHeight));
setHostedComponentBounds();
}
public void setLocation(Point2D p){
setBounds(p.getX(), p.getY(), width, height);
}
public void setLocation(double newX, double newY){
setBounds(newX, newY, width, height);
}
public void translate(double dx, double dy){
setBounds(x+dx, y+dy, width, height);
}
public void setSize(Dimension2D d){
setBounds(x, y, d.getWidth(), d.getHeight());
}
public void setSize(double newWidth, double newHeight){
setBounds(x, y, newWidth, newHeight);
}
public Point2D getLocation(){
return new java.awt.geom.Point2D.Double(x, y);
}
protected void checkHostedComponent(){
// to override
}
public Point2D getAbsoluteLocation(){
Point2D result=null;
JComponent jc=getContainerResolve();
if(jc!=null){
result=new java.awt.Point(jc.getLocationOnScreen());
result.setLocation(result.getX() + getX(), result.getY() + getY());
}
return result;
}
public Point2D getAbsoulteMiddleLocation(){
Point2D result=getAbsoluteLocation();
if(result!=null){
result.setLocation(result.getX() + getWidth()/2, result.getY() + getHeight()/2);
}
return result;
}
}