/*
* File : SimpleAssociation.java
* Created : 20-apr-2001 17:40
* 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.activities.associations;
import edu.xtec.jclic.*;
import edu.xtec.jclic.automation.ActiveBagContentKit;
import edu.xtec.jclic.boxes.*;
import edu.xtec.jclic.clic3.Clic3Activity;
import edu.xtec.jclic.media.EventSounds;
import edu.xtec.jclic.project.JClicProject;
import edu.xtec.util.JDomUtility;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.Iterator;
/**
* This activity has two {@link edu.xtec.jclic.bags.MediaBag} objects with the same number of elements.
* The elements are linked one to one (first A to first B, second A to second B,
* etc).
* @author Francesc Busquets (fbusquets@xtec.cat)
* @version 13.08.28
* @see Activity
*/
public class SimpleAssociation extends Activity implements ActiveBagContentKit.Compatible {
// for internal use
boolean useIdAss;
/**
* Creates new simpleAssociation
* @param project The project to wich this activity belongs.
*/
public SimpleAssociation(JClicProject project) {
super(project);
boxGridPos=AB;
scramble[0]=true;
scramble[1]=true;
abc=new ActiveBagContent[3];
//for(int i=0; i<3; i++)
// abc[i]=null;
useIdAss=false;
}
/**
* Initialisation method for newly created activities.
*/
@Override
public void initNew(){
super.initNew();
abc[0]=ActiveBagContent.initNew(3, 2, 'A');
abc[1]=ActiveBagContent.initNew(3, 2, '1');
//abc[2]=ActiveBagContent.initNew(3, 2, 'a');
}
/**
* Creates a JDom Element that represents the current object properties, in order
* to allow it to be stored into a XML files.
* @return A JDom element representing the activity properties.
*/
@Override
public org.jdom.Element getJDomElement(){
org.jdom.Element ex;
if(abc[0]==null || abc[1]==null)
return null;
org.jdom.Element e=super.getJDomElement();
e.addContent(abc[0].getJDomElement().setAttribute(ID, PRIMARY));
e.addContent(abc[1].getJDomElement().setAttribute(ID, SECONDARY));
if(abc[2]!=null)
e.addContent(abc[2].getJDomElement().setAttribute(ID, SOLVED_PRIMARY));
ex=new org.jdom.Element(SCRAMBLE);
ex.setAttribute(TIMES, Integer.toString(shuffles));
ex.setAttribute(PRIMARY, JDomUtility.boolString(scramble[0]));
ex.setAttribute(SECONDARY, JDomUtility.boolString(scramble[1]));
e.addContent(ex);
ex=new org.jdom.Element(LAYOUT);
ex.setAttribute(POSITION, LAYOUT_NAMES[boxGridPos]);
e.addContent(ex);
return e;
}
/**
* Applies to this object the properties previously stored into a JDom Element
* usually created by means of a call to the {@link #getJDomElement()} method).
* @param e The JDom element cointaining the data.
* @param aux Auxiliary object. Unused here, but has to be {@link edu.xtec.util.Domable}.
* @throws Exception If something goes wrong.
*/
@Override
public void setProperties(org.jdom.Element e, Object aux) throws Exception{
org.jdom.Element child;
super.setProperties(e, aux);
ActiveBagContent bag;
abc[2]=null;
Iterator itr = e.getChildren(ActiveBagContent.ELEMENT_NAME).iterator();
while (itr.hasNext()){
child=((org.jdom.Element)itr.next());
bag=ActiveBagContent.getActiveBagContent(child, project.mediaBag);
String id=JDomUtility.getStringAttr(child, ID, PRIMARY, false);
if(PRIMARY.equals(id))
abc[0]=bag;
else if(SECONDARY.equals(id))
abc[1]=bag;
else if(SOLVED_PRIMARY.equals(id))
abc[2]=bag;
}
if(abc[0]==null || abc[1]==null)
throw new IllegalArgumentException("Association without contents!");
if((child=e.getChild(SCRAMBLE))!=null){
shuffles=JDomUtility.getIntAttr(child, TIMES, shuffles);
scramble[0]=JDomUtility.getBoolAttr(child, PRIMARY, scramble[0]);
scramble[1]=JDomUtility.getBoolAttr(child, SECONDARY, scramble[1]);
}
if((child=e.getChild(LAYOUT))!=null)
boxGridPos=JDomUtility.getStrIndexAttr(child, POSITION, LAYOUT_NAMES, boxGridPos);
}
@Override
public void setProperties(Clic3Activity c3a) throws Exception{
super.setProperties(c3a);
// Clic3 problem
if(getClass().getSuperclass()==Activity.class){
c3a.nctxw=c3a.ncw;
c3a.nctxh=c3a.nch;
}
// -----
boxGridPos=c3a.graPos;
for(int i=0; i<2; i++){
abc[i]=c3a.createActiveBagContent(i);
abc[i].setBoxBase(c3a.getBoxBase(i));
scramble[i]=c3a.bar[i];
}
abc[2]=c3a.sol ? c3a.createActiveBagContent(2) : null;
}
public int getMinNumActions(){
return abc[0]==null ? 0 : abc[0].getNumCells();
}
@Override
public boolean helpSolutionAllowed(){
return true;
}
@Override
public boolean hasRandom(){
return true;
}
public Activity.Panel getActivityPanel(PlayStation ps) {
return new Panel(ps);
}
class Panel extends Activity.Panel {
ActiveBoxBag[] bg=new ActiveBoxBag[2];
protected Panel(PlayStation ps){
super(ps);
bc=new BoxConnector(this);
for(int i=0; i<2; i++){
bg[i]=null;
}
currentItem=0;
}
public void clear(){
for(int i=0; i<2; i++)
if(bg[i]!=null){
bg[i].end();
bg[i]=null;
}
}
@Override
public void buildVisualComponents() throws Exception {
if(firstRun) super.buildVisualComponents();
clear();
if(abc[0]!=null && abc[1]!=null){
if(acp!=null)
acp.generateContent(new ActiveBagContentKit(abc[0].nch, abc[0].ncw, abc, false), ps);
for(int i=0; i<2; i++)
bg[i]=ActiveBoxGrid.createEmptyGrid(null, this, margin, margin, abc[i]);
for(int i=0; i<2; i++){
bg[i].setContent(abc[i], i==0 ? abc[2] : null);
bg[i].setVisible(true);
}
invalidate();
}
}
@Override
public void initActivity() throws Exception{
super.initActivity();
if(!firstRun)
buildVisualComponents();
else
firstRun=false;
//ps.setMsg(messages[MAIN]);
setAndPlayMsg(MAIN, EventSounds.START);
if(bg[0]!=null && bg[1]!=null){
if(scramble[0] || scramble[1])
shuffle(new ActiveBoxBag[]{scramble[0] ? bg[0]:null, scramble[1] ? bg[1]:null}, true, true);
//ps.playMsg();
//if(messages[MAIN]==null || messages[MAIN].mediaContent==null)
// playEvent(EventSounds.START);
if(useOrder)
currentItem=bg[0].getNextItem(-1);
playing=true;
}
}
public void render(Graphics2D g2, Rectangle dirtyRegion) {
for(int i=0; i<2; i++)
if(bg[i]!=null)
bg[i].update(g2, dirtyRegion, this);
if(bc.active)
bc.update(g2, dirtyRegion, this);
}
public Dimension setDimension(Dimension preferredMaxSize){
if(bg[0]==null || bg[1]==null || getSize().equals(preferredMaxSize))
return preferredMaxSize;
return BoxBag.layoutDouble(preferredMaxSize, bg[0], bg[1], boxGridPos, margin);
}
@Override
public void processMouse(MouseEvent e){
ActiveBox bx1, bx2;
Point p=e.getPoint();
boolean m=false;
if(playing) switch(e.getID()){
case MouseEvent.MOUSE_PRESSED:
ps.stopMedia(1);
if(bc.active){
boolean clickOnBg0=false;
bc.end();
if((bx1=bg[0].findActiveBox(bc.origin))!=null)
bx2=bg[1].findActiveBox(/*bc.dest*/p);
else if((bx2=bg[1].findActiveBox(bc.origin))!=null){
bx1=bg[0].findActiveBox(/*bc.dest*/p);
clickOnBg0=true;
}
if(bx1!=null && bx2!=null && bx1.idAss!=-1 && bx2.idAss!=-1){
boolean ok=false;
String src=bx1.getDescription();
String dest=bx2.getDescription();
if(bx1.idOrder==bx2.idOrder
||(bx2.getContent().isEquivalent(abc[1].getActiveBoxContent(bx1.idOrder), true))){
ok=true;
bx1.setIdAss(-1);
bx2.setIdAss(-1);
if(abc[2]!=null){
bx1.switchToAlt(ps);
m|=bx1.playMedia(ps);
}
else{
if(clickOnBg0) m|=bx1.playMedia(ps);
else m|=bx2.playMedia(ps);
bx1.clear();
}
bx2.clear();
if(useOrder)
currentItem=bg[0].getNextItem(currentItem);
}
int cellsPlaced=bg[1].countCellsWithIdAss(-1);
ps.reportNewAction(getActivity(), ACTION_MATCH, src, dest, ok, cellsPlaced);
if(ok && cellsPlaced==bg[1].getNumCells())
finishActivity(true);
else if(!m)
playEvent(ok ? EventSounds.ACTION_OK : EventSounds.ACTION_ERROR);
}
}
else{
if(((bx1=bg[0].findActiveBox(p))!=null && (!useOrder || bx1.idOrder==currentItem))
||(!useOrder && (bx1=bg[1].findActiveBox(p))!=null) && bx1.idAss!=-1){
if(dragCells)
bc.begin(p, bx1);
else
bc.begin(p);
m=bx1.playMedia(ps);
if(!m) playEvent(EventSounds.CLICK);
}
}
break;
case MouseEvent.MOUSE_MOVED:
case MouseEvent.MOUSE_DRAGGED:
if(bc.active)
bc.moveTo(p);
break;
}
}
@Override
public void showHelp(){
if(!helpWindowAllowed() || bg[0]==null)
return;
HelpActivityComponent hac=null;
if(showSolution){
hac=new HelpActivityComponent(this){
ActiveBoxBag abb=null;
int cellsPlaced=bg[1].countCellsWithIdAss(-1);
public void render(Graphics2D g2, Rectangle dirtyRegion){
if(abb!=null) abb.update(g2, dirtyRegion, this);
}
@Override
public void init(){
abb=(ActiveBoxBag)bg[0].clone();
abb.setContainer(this);
Dimension size=abb.getBounds().getSize();
abb.setBounds(DEFAULT_MARGIN, DEFAULT_MARGIN, size.width, size.height);
size.width+=2*DEFAULT_MARGIN;
size.height+=2*DEFAULT_MARGIN;
setPreferredSize(size);
setMaximumSize(size);
setMinimumSize(size);
Point p=(Point)getClientProperty(HelpActivityComponent.PREFERRED_LOCATION);
if(p!=null)
p.translate((int)bg[0].x-DEFAULT_MARGIN, (int)bg[0].y-DEFAULT_MARGIN);
}
@Override
public void processMouse(MouseEvent e){
ActiveBox bx;
boolean m;
if(abb!=null) switch(e.getID()){
case MouseEvent.MOUSE_PRESSED:
bx=abb.findActiveBox(e.getPoint());
if(bx!=null){
m=bx.playMedia(ps);
if(!m) playEvent(EventSounds.CLICK);
if(bx.idAss>=0){
ActiveBox bxSolution=bg[1].getActiveBox(useIdAss ? bx.idAss : bx.idOrder);
markBox(bxSolution, false);
if(bxSolution!=null)
ps.reportNewAction(getActivity(), ACTION_HELP, bx.getDescription(), bxSolution.getDescription(), false, cellsPlaced);
}
}
break;
case MouseEvent.MOUSE_RELEASED:
unmarkBox();
break;
}
}
};
hac.init();
}
if(ps.showHelp(hac, helpMsg))
ps.reportNewAction(getActivity(), ACTION_HELP, null, null, false, bg[1].countCellsWithIdAss(-1));
if(hac!=null)
hac.end();
}
}
}