/* * File : Activity.java * Created : 16-aug-2001 1:05 * 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; import edu.xtec.jclic.automation.AutoContentProvider; import edu.xtec.jclic.bags.MediaBagElement; import edu.xtec.jclic.boxes.ActiveBagContent; import edu.xtec.jclic.boxes.ActiveBoxBag; import edu.xtec.jclic.boxes.ActiveBoxContent; import edu.xtec.jclic.boxes.BoxBase; import edu.xtec.jclic.boxes.BoxConnector; import edu.xtec.jclic.boxes.TextGridContent; import edu.xtec.jclic.clic3.Clic3Activity; import edu.xtec.jclic.edit.Editable; import edu.xtec.jclic.edit.Editor; import edu.xtec.jclic.media.EventSounds; import edu.xtec.jclic.misc.Gradient; import edu.xtec.jclic.project.JClicProject; import edu.xtec.jclic.skins.Skin; import edu.xtec.util.Domable; import edu.xtec.util.Html; import edu.xtec.util.JDomUtility; import edu.xtec.util.ResourceBridge; import java.awt.AWTEvent; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.lang.reflect.Constructor; import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import javax.swing.BorderFactory; import javax.swing.JPanel; import javax.swing.border.BevelBorder; /** * <CODE>Activity</CODE> is the abstract base class for JClic activities. It * defines also the inner class {@link edu.xtec.jclic.Activity.Panel}, wich is * responsible of the user interaction with the activity content. * Activities should extend both <CODE>Activity</CODE> and * <CODE>Activity.Panel</CODE> classes in order to become fully operative. * JClic stores activities in memory as {@link org.jdom.Element} objects. So, * all non-transient data must be stored to and retrieved from JDom elements. * @author Francesc Busquets (fbusquets@xtec.cat) * @version 13.09.10 */ public abstract class Activity extends Object implements Constants, Editable, Domable { /** * The {@link JClicProject} this activity belongs to. */ protected JClicProject project; // public fields /** * The activity name */ public String name=DEFAULT_NAME; /** * Code used in reports to filter queries. Default is <I>null</I>. */ public String code; /** * Description of the activity. */ public String description; /** * Space, measured in pixels, between the activity components. */ public int margin=DEFAULT_MARGIN; /** * Background color of the activity panel. */ public Color bgColor=DEFAULT_BG_COLOR; /** * Gradient used to draw the background of the activity panel. */ public Gradient bgGradient; /** * Whether the bgImage (if any) has to be tiled across the panel background. */ public boolean tiledBgImg; /** * Filename of the image painted in the panel background. Default is <I>null</I>. */ public String bgImageFile; /** * Whether to draw a border around the activity panel. */ public boolean border=true; /** * Whether to place the activity panel at the point specified by {@link * absolutePosition}, or leave it centered in the main window of the player. */ public boolean absolutePositioned; /** Position of the activity panel into the {@link edu.xtec.jclic.Player}. */ public Point absolutePosition; /** * Whether to generate usage reports. */ public boolean includeInReports=true; /** * Whether to send action events to the {@link edu.xtec.jclic.report.Reporter}. */ public boolean reportActions; /** * Whether to have a help window or not. */ public boolean helpWindow; /** * Whether to show the solution on the help window. */ public boolean showSolution; /** * Message to show in the help window when {@link showSolution} is <CODE>false</CODE>. */ public String helpMsg; /** * Specific set of {@link EventSounds} used in the activity. The default is <CODE>null</CODE>, * meaning to use the {@link edu.xtec.jclic.Player} sounds. */ public EventSounds eventSounds=new EventSounds(null); /** * Wheter the activity must be solved in a specific order. The default is <CODE>false</CODE>. */ public boolean useOrder; /** * Wheter the cells of the activity will be dragged across the screen. When <CODE>false</CODE>, * a line will be painted to link elements. */ public boolean dragCells; /** * File name of the Skin used by the activity. The default value is null, meaning * that the activity will use the skin specified for the project. */ public String skinFileName; /** * Maximum amount of time (seconds) to solve the activity. The default value is <CODE>0</CODE>, * meaning unlimited time. */ public int maxTime; /** * Whether the time counter should display a countdown when {@link #maxTime} > 0. */ public boolean countDownTime; /** * Maximum number of actions allowed to solve the activity. The default value is <CODE>0</CODE>, * meaning unlimited actions. */ public int maxActions; /** * Whether the actions counter should display a countdown when {@link #maxActions} > 0. */ public boolean countDownActions; /** * String with the URL to be displayed when the user clicks on the <I>info</I> button. * Default is <CODE>null</CODE>. */ public String infoUrl; /** * System command to be executed when the user clicks the <I>info</I> button. Default is * <CODE>null</CODE>. Applets have this function disabled. */ public String infoCmd; // protected fields /** * String labels corresponding to the four identifiers of JClic messages: * PREVIOUS, MAIN, END and END_ERROR. */ public static final String[] MSG_TYPE={"previous", "initial", "final", "finalError"}; /** * Identifier of the message displayed before the activity starts. Used only in * certain types of activities. */ public static final int PREVIOUS = 0; /** * Identifier of the main message, displayed as long as the activity is playing. */ public static final int MAIN = 1; /** * Identifier of the message displayed when the user successfully ends the * activity. */ public static final int END = 2; /** * Identifier of the message displayed when the user exceeds the maximum amount of time * or actions allowed. * @see maxTime * @see maxActions */ public static final int END_ERROR = 3; /** * Number of message types. */ public static final int NUM_MSG=4; protected ActiveBoxContent[] messages=new ActiveBoxContent[NUM_MSG]; protected Dimension windowSize=new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); protected boolean transparentBg; protected Color activityBgColor=DEFAULT_BG_COLOR; protected Gradient activityBgGradient; protected boolean bTimeCounter=true; protected boolean bScoreCounter=true; protected boolean bActionsCounter=true; protected int shuffles=DEFAULT_SHUFFLES; protected AutoContentProvider acp; // fields used only by certain activity types public ActiveBagContent[] abc; public TextGridContent tgc; public int boxGridPos; public boolean[] scramble=new boolean[2]; public boolean invAss; // static fields // layout constants public static final int AB=0, BA=1, AUB=2, BUA=3; public static final String[] LAYOUT_NAMES={"AB", "BA", "AUB", "BUA"}; public static final int DEFAULT_WIDTH=400; public static final int DEFAULT_HEIGHT=300; public static final int MINIMUM_WIDTH=40; public static final int MINIMUM_HEIGHT=40; public static final String DEFAULT_NAME="---"; public static final int DEFAULT_MARGIN=8; public static final int DEFAULT_SHUFFLES=31; public static final int DEFAULT_GRID_ELEMENT_SIZE=20; public static final Color DEFAULT_BG_COLOR=Color.lightGray; // Constants for actions public static final String ACTION_MATCH="MATCH", ACTION_PLACE="PLACE", ACTION_WRITE="WRITE", ACTION_SELECT="SELECT", ACTION_HELP="HELP"; // Constants for XML fields public static final String ELEMENT_NAME="activity", NAME="name"; public static final String BASE_CLASS="edu.xtec.jclic.activities.", BASE_CLASS_TAG="@"; // Already defined in edu.xtec.jclic.Constants: // public static final String ID="id", SKIN="skin"; public static final String CODE="code", DESCRIPTION="description", MESSAGES="messages", TYPE="type", //PREVIOUS="previous", INITIAL="initial", FINAL="final", FINAL_ERROR="finalError", SETTINGS="settings", LAYOUT="layout", MARGIN="margin", CONTAINER="container", BGCOLOR="bgColor", IMAGE="image", TILED="tiled", COUNTERS="counters", TIME="time", ACTIONS="actions", SCORE="score", WINDOW="window", TRANSPARENT="transparent", BORDER="border", POSITION="position", X="x", Y="y", SIZE="size", WIDTH="width", HEIGHT="height", PRIMARY="primary", SECONDARY="secondary", SOLVED_PRIMARY="solvedPrimary", SOLVED_SECONDARY="solved_secondary", GRID="grid", ROW="row", CLUES="clues", CLUE="clue", RANDOM_CHARS="random_chars", SCRAMBLE="scramble", TIMES="times", REPORT="report", REPORT_ACTIONS="reportActions", HELP_WINDOW="helpWindow", HELP_SHOW_SOLUTION="showSolution", USE_ORDER="useOrder", DRAG_CELLS="dragCells", FILE="file", MAX_TIME="maxTime", COUNT_DOWN_TIME="countDownTime", MAX_ACTIONS="maxActions", COUNT_DOWN_ACTIONS="countDownActions", INFO_URL="infoUrl", INFO_CMD="infoCmd"; public static final String[][] COMPATIBLE_ACTIVITIES = new String[][]{ new String[]{ "@puzzles.DoublePuzzle", "@puzzles.ExchangePuzzle", "@puzzles.HolePuzzle", "@memory.MemoryGame", "@associations.SimpleAssociation", "@associations.ComplexAssociation", "@panels.Explore", "@panels.Identify", "@panels.InformationScreen", "@text.WrittenAnswer" }, new String[]{ "@text.FillInBlanks", "@text.Identify", "@text.Order", "@text.Complete", } }; /** Creates new edu.xtec.jclicActivity */ public Activity(JClicProject project) { this.project=project; } public void initNew(){ name=project.getBridge().getMsg("UNNAMED"); } public String getPublicName(){ return name; } public JClicProject getProject(){ return project; } public Editor getEditor(Editor parent){ Editor result=null; String s=getClass().getName()+"Editor"; try{ Class.forName(s); } catch(ClassNotFoundException ex){ s="edu.xtec.jclic.ActivityEditor"; } return Editor.createEditor(s, this, parent); } public String getShortClassName(){ String s=getClass().getName(); if(s.startsWith(BASE_CLASS)) s=BASE_CLASS_TAG+s.substring(BASE_CLASS.length()); return s; } public String[] getSimilarActivityClasses(){ String[] result=null; String cl=getShortClassName(); for(String[] classGroup : COMPATIBLE_ACTIVITIES){ for(String s : classGroup){ if(cl.equals(s)){ result=classGroup; break; } } } if(result==null){ result=new String[]{cl}; } return result; } public org.jdom.Element getJDomElement(){ org.jdom.Element child, child2, child3; org.jdom.Element e=new org.jdom.Element(ELEMENT_NAME); e.setAttribute(JDomUtility.CLASS, getShortClassName()); e.setAttribute(NAME, name); if(code!=null) e.setAttribute(CODE, code); if(description!=null) JDomUtility.addParagraphs(e, DESCRIPTION, description); child=new org.jdom.Element(MESSAGES); for(int i=0; i<NUM_MSG; i++) if(messages[i]!=null) child.addContent(messages[i].getJDomElement().setAttribute(TYPE, MSG_TYPE[i])); e.addContent(child); child=new org.jdom.Element(SETTINGS); child.setAttribute(MARGIN, Integer.toString(margin)); if(infoUrl!=null && infoUrl.length()>0) child.setAttribute(INFO_URL, infoUrl); else if(infoCmd!=null && infoCmd.length()>0) child.setAttribute(INFO_CMD, infoCmd); if(useOrder) child.setAttribute(USE_ORDER, JDomUtility.boolString(useOrder)); if(dragCells) child.setAttribute(DRAG_CELLS, JDomUtility.boolString(dragCells)); if(maxTime>0){ child.setAttribute(MAX_TIME, Integer.toString(maxTime)); child.setAttribute(COUNT_DOWN_TIME, JDomUtility.boolString(countDownTime)); } if(maxActions>0){ child.setAttribute(MAX_ACTIONS, Integer.toString(maxActions)); child.setAttribute(COUNT_DOWN_ACTIONS, JDomUtility.boolString(countDownActions)); } child.setAttribute(REPORT, JDomUtility.boolString(includeInReports)); if(includeInReports) child.setAttribute(REPORT_ACTIONS, JDomUtility.boolString(reportActions)); if(helpWindow){ child2=new org.jdom.Element(HELP_WINDOW); boolean hsa=helpSolutionAllowed(); if(hsa) child2.setAttribute(HELP_SHOW_SOLUTION, JDomUtility.boolString(showSolution)); if(helpMsg!=null && (!hsa || !showSolution)) JDomUtility.setParagraphs(child2, helpMsg); child.addContent(child2); } child2=new org.jdom.Element(CONTAINER); child2.setAttribute(BGCOLOR, JDomUtility.colorToString(bgColor)); if(bgGradient!=null) child2.addContent(bgGradient.getJDomElement()); if(bgImageFile!=null && bgImageFile.length()>0){ child3=new org.jdom.Element(IMAGE); child3.setAttribute(NAME, bgImageFile); child3.setAttribute(TILED, JDomUtility.boolString(tiledBgImg)); child2.addContent(child3); } child3=new org.jdom.Element(COUNTERS); child3.setAttribute(TIME, JDomUtility.boolString(bTimeCounter)); child3.setAttribute(ACTIONS, JDomUtility.boolString(bActionsCounter)); child3.setAttribute(SCORE, JDomUtility.boolString(bScoreCounter)); child2.addContent(child3); child.addContent(child2); child2=new org.jdom.Element(WINDOW); child2.setAttribute(BGCOLOR, JDomUtility.colorToString(activityBgColor)); if(activityBgGradient!=null) child2.addContent(activityBgGradient.getJDomElement()); if(transparentBg) child2.setAttribute(TRANSPARENT, JDomUtility.boolString(transparentBg)); child2.setAttribute(BORDER, JDomUtility.boolString(border)); if(absolutePositioned && absolutePosition!=null){ child3=new org.jdom.Element(POSITION); child3.setAttribute(X, Integer.toString(absolutePosition.x)); child3.setAttribute(Y, Integer.toString(absolutePosition.y)); child2.addContent(child3); } if(windowSize.width!=DEFAULT_WIDTH || windowSize.height!=DEFAULT_HEIGHT){ child3=new org.jdom.Element(SIZE); child3.setAttribute(WIDTH, Integer.toString(windowSize.width)); child3.setAttribute(HEIGHT, Integer.toString(windowSize.height)); child2.addContent(child3); } child.addContent(child2); if((child2=eventSounds.getJDomElement())!=null) child.addContent(child2); if(skinFileName!=null){ child2=new org.jdom.Element(SKIN); child2.setAttribute(FILE, skinFileName); //if(skin!=null && skin.name!=null && skin.name.length()>0) // child2.setAttribute(NAME, skin.name); child.addContent(child2); } e.addContent(child); if(acp!=null) e.addContent(acp.getJDomElement()); return e; } public static Activity getActivity(Object o, JClicProject project) throws Exception{ Activity act; org.jdom.Element e = null; Clic3Activity c3a = null; String className; //if(o.getClass()==org.jdom.Element.class){ if(o instanceof org.jdom.Element){ e=(org.jdom.Element)o; JDomUtility.checkName(e, ELEMENT_NAME); className=JDomUtility.getClassName(e); } //else if(o.getClass()==Clic3Activity.class){ else if(o instanceof Clic3Activity){ c3a=(Clic3Activity)o; className=c3a.className; } else if(o instanceof String){ className=(String)o; } else throw new Exception("unknown data!!"); Class<?> activityClass; Constructor<?> con; Class<?>[] cparams={JClicProject.class}; Object[] initArgs={project}; if(className.startsWith(BASE_CLASS_TAG)) className=BASE_CLASS+className.substring(1); activityClass=Class.forName(className); con=activityClass.getConstructor(cparams); act=(Activity)con.newInstance(initArgs); if(e!=null) act.setProperties(e, null); else if(c3a!=null) act.setProperties(c3a); else act.initNew(); return act; } public Activity duplicate() throws Exception{ return Activity.getActivity(getJDomElement(), project); } public void setProperties(org.jdom.Element e, Object aux) throws Exception{ org.jdom.Element child, child2, child3; JDomUtility.checkName(e, ELEMENT_NAME); name=JDomUtility.getStringAttr(e, NAME, name, false); code=JDomUtility.getStringAttr(e, CODE, code, false); if((child=e.getChild(DESCRIPTION))!=null) description=JDomUtility.getParagraphs(child); if((child=e.getChild(MESSAGES))!=null){ Iterator itr = child.getChildren(ActiveBoxContent.ELEMENT_NAME).iterator(); while (itr.hasNext()){ child2=((org.jdom.Element)itr.next()); int i=JDomUtility.getStrIndexAttr(child2, TYPE, MSG_TYPE, -1); if(i>=0) messages[i]=ActiveBoxContent.getActiveBoxContent(child2, project.mediaBag); } } if((child=e.getChild(SETTINGS))!=null){ margin=JDomUtility.getIntAttr(child, MARGIN, margin); infoUrl=JDomUtility.getStringAttr(child, INFO_URL, infoUrl, false); if(infoUrl==null) infoCmd=JDomUtility.getStringAttr(child, INFO_CMD, infoCmd, false); useOrder=JDomUtility.getBoolAttr(child, USE_ORDER, useOrder); dragCells=JDomUtility.getBoolAttr(child, DRAG_CELLS, dragCells); maxTime=JDomUtility.getIntAttr(child, MAX_TIME, maxTime); if(maxTime>0) countDownTime=JDomUtility.getBoolAttr(child, COUNT_DOWN_TIME, countDownTime); maxActions=JDomUtility.getIntAttr(child, MAX_ACTIONS, maxActions); if(maxActions>0) countDownActions=JDomUtility.getBoolAttr(child, COUNT_DOWN_ACTIONS, countDownActions); includeInReports=JDomUtility.getBoolAttr(child, REPORT, includeInReports); if(includeInReports) reportActions=JDomUtility.getBoolAttr(child, REPORT_ACTIONS, reportActions); else reportActions=false; if((child2=child.getChild(HELP_WINDOW))!=null){ if(helpSolutionAllowed()){ showSolution=JDomUtility.getBoolAttr(child2, HELP_SHOW_SOLUTION, showSolution); } if(!showSolution) helpMsg=JDomUtility.getParagraphs(child2); helpWindow = helpMsg!=null || showSolution; } if((child2=child.getChild(CONTAINER))!=null){ bgColor=JDomUtility.getColorAttr(child2, BGCOLOR, bgColor); if((child3=child2.getChild(Gradient.ELEMENT_NAME))!=null) bgGradient=Gradient.getGradient(child3); if((child3=child2.getChild(IMAGE))!=null){ bgImageFile=child3.getAttributeValue(NAME); tiledBgImg=JDomUtility.getBoolAttr(child3, TILED, tiledBgImg); } if((child3=child2.getChild(COUNTERS))!=null){ bTimeCounter=JDomUtility.getBoolAttr(child3, TIME, bTimeCounter); bActionsCounter=JDomUtility.getBoolAttr(child3, ACTIONS, bActionsCounter); bScoreCounter=JDomUtility.getBoolAttr(child3, SCORE, bScoreCounter); // Check for old version bug //String v=rb.getOptions().getString(CURRENT_PROJECT_VERSION); String v=project.version; if(v!=null && v.compareTo("0.1.1")<=0){ boolean b=bScoreCounter; bScoreCounter=bTimeCounter; bTimeCounter=bActionsCounter; bActionsCounter=b; } } } if((child2=child.getChild(WINDOW))!=null){ activityBgColor=JDomUtility.getColorAttr(child2, BGCOLOR, activityBgColor); if((child3=child2.getChild(Gradient.ELEMENT_NAME))!=null) activityBgGradient=Gradient.getGradient(child3); transparentBg=JDomUtility.getBoolAttr(child2, TRANSPARENT, transparentBg); border=JDomUtility.getBoolAttr(child2, BORDER, border); if((child3=child2.getChild(POSITION))!=null){ absolutePositioned=true; absolutePosition=JDomUtility.getPointAttr(child3, X, Y, absolutePosition); } if((child3=child2.getChild(SIZE))!=null){ windowSize=JDomUtility.getDimensionAttr(child3, WIDTH, HEIGHT, windowSize); } } if((child2=child.getChild(EventSounds.ELEMENT_NAME))!=null) eventSounds=EventSounds.getEventSounds(child2); if((child2=child.getChild(SKIN))!=null){ skinFileName=JDomUtility.getStringAttr(child2, FILE, skinFileName, false); /* if(skinFileName!=null && skinFileName.length()>0){ if(project.getBridge() instanceof PlayStation) skin=project.mediaBag.getSkinElement(skinFileName, (PlayStation)project.getBridge()); } else skinFileName=null; */ } } if((child=e.getChild(AutoContentProvider.ELEMENT_NAME))!=null){ acp=AutoContentProvider.getAutoContentProvider(child); } } public void setProperties(Clic3Activity c3a) throws Exception{ name=c3a.fileName; if(c3a.fileDesc.length()>0) description=c3a.fileDesc; BoxBase bbMessage=c3a.getBoxBase(2); for(int i=MAIN; i<END_ERROR; i++){ messages[i]=new ActiveBoxContent(); messages[i].setBoxBase(bbMessage); c3a.setActiveBoxTextContent(messages[i], i==MAIN ? c3a.initMess : c3a.endMess); if(messages[i].mediaContent!=null) messages[i].mediaContent.level=2; } /* messages[MAIN]=new ActiveBoxContent(); messages[MAIN].setBoxBase(bbMessage); c3a.setActiveBoxTextContent(messages[MAIN], c3a.initMess); if(messages[MAIN].mediaContent!=null) messages[MAIN].mediaContent.level=2; messages[END]=new ActiveBoxContent(); messages[END].setBoxBase(bbMessage); c3a.setActiveBoxTextContent(messages[END], c3a.endMess); if(messages[END].mediaContent!=null) messages[END].mediaContent.level=2; */ absolutePositioned=c3a.pwrp; absolutePosition=new Point(c3a.pwrx, c3a.pwry); transparentBg=c3a.pwTransp; //activityBgPattern=c3a.tramaFons[1]; activityBgColor=c3a.colorFons[1]; border=c3a.marcs; //bgPattern=c3a.tramaFons[0]; bgImageFile=c3a.bmpFons; if(bgImageFile!=null && bgImageFile.length()>0) project.mediaBag.getImageElement(bgImageFile); tiledBgImg=c3a.tileBmp; bgColor=c3a.colorFons[0]; bTimeCounter=c3a.comptadors[0]; bScoreCounter=c3a.comptadors[1]; bActionsCounter=c3a.comptadors[2]; includeInReports=!c3a.noAv; if(c3a.custHlp==true && c3a.custHelpFile!=null && c3a.custHelpFile.length()>0){ StringBuilder sb=new StringBuilder(); sb.append(c3a.custHelpFile); if(c3a.hlpTopic!=null && c3a.hlpTopic.length()>0) sb.append(" ").append(c3a.hlpTopic); String s=sb.substring(0); if(s.indexOf(".htm")>0){ StringTokenizer st=new StringTokenizer(s, " "); while(st.hasMoreTokens()){ String r=st.nextToken(); if(r.indexOf(".htm")>0){ infoUrl=r; break; } } } else{ if(c3a.custHelpFile.toLowerCase().indexOf(".hlp")>0) infoCmd="winhelp.exe "+c3a.custHelpFile; else infoCmd=s; } } if(helpSolutionAllowed()) showSolution=c3a.shHelp; if(showSolution) helpWindow=true; if(c3a.useDLL){ if(c3a.rgDLL.equalsIgnoreCase("arith2.dll")){ org.jdom.Element e=new org.jdom.Element(AutoContentProvider.ELEMENT_NAME); e.setAttribute(JDomUtility.CLASS, "edu.xtec.jclic.automation.arith.Arith"); try{ acp=AutoContentProvider.getAutoContentProvider(e); if(acp!=null) acp.setClic3Properties(c3a.dllOptions); }catch(Exception ex){ System.err.println("Unable to start arith2:\n"+ex); } } } } public static void listReferences(org.jdom.Element e, Map<String, String> map){ org.jdom.Element child, child2, child3; if((child=e.getChild(MESSAGES))!=null){ Iterator itr = child.getChildren(ActiveBoxContent.ELEMENT_NAME).iterator(); while(itr.hasNext()) ActiveBoxContent.listReferences((org.jdom.Element)itr.next(), map); } if((child=e.getChild(SETTINGS))!=null){ if((child2=child.getChild(CONTAINER))!=null){ if((child3=child2.getChild(IMAGE))!=null){ map.put(child3.getAttributeValue(NAME), Constants.MEDIA_OBJECT); } } if((child2=child.getChild(EventSounds.ELEMENT_NAME))!=null) EventSounds.listReferences(child2, map); if((child2=child.getChild(SKIN))!=null){ String skfn=JDomUtility.getStringAttr(child2, FILE, null, false); if(skfn!=null && !skfn.startsWith(Skin.INTERNAL_SKIN_PREFIX)) map.put(skfn, Constants.SKIN_OBJECT); // check skin dependences... } } Iterator itr = e.getChildren(ActiveBagContent.ELEMENT_NAME).iterator(); while(itr.hasNext()) ActiveBagContent.listReferences((org.jdom.Element)itr.next(), map); if((child=e.getChild("document"))!=null){ //if((child=e.getChild(TextActivityDocument.ELEMENT_NAME))!=null){ //TextActivityDocument.listReferences(child, map); innerListReferences(child, map); } if((child=e.getChild(AutoContentProvider.ELEMENT_NAME))!=null){ try{ AutoContentProvider.listReferences(child, map); } catch(Exception ex){ System.err.println("Error checking AutoContentProvider: "+ex); } } } public static void innerListReferences(org.jdom.Element e, Map<String, String> map){ Iterator it=e.getChildren(ActiveBoxContent.ELEMENT_NAME).iterator(); while(it.hasNext()) ActiveBoxContent.listReferences((org.jdom.Element)it.next(), map); it=e.getChildren().iterator(); while(it.hasNext()){ org.jdom.Element child=(org.jdom.Element)it.next(); if(!ActiveBoxContent.ELEMENT_NAME.equals(child.getName())) innerListReferences(child, map); } } public ActiveBoxContent[] getMessages(){ return messages; } public String toHtmlString(ResourceBridge rb){ Html html=new Html(1000); html.doubleCell(rb.getMsg("about_window_lb_activity"), true, name, true); if(description!=null) html.doubleCell(rb.getMsg("about_window_lb_description"), true, description, false); return Html.table(html.toString(), null, 1, 5, -1, null, false); } // 02-oct-2006: Param "ResourceBridge" removed (can be obtained from "project"), // Changed the call to "init" public void initAutoContentProvider(){ if(acp!=null) acp.init(project.getBridge(), project.getFileSystem()); } public boolean prepareMedia(PlayStation ps){ if(eventSounds!=null) eventSounds.realize(ps.getOptions(), project.mediaBag); for(ActiveBoxContent msg : messages) if(msg!=null) msg.prepareMedia(ps); if(abc!=null) for(ActiveBagContent b : abc) if(b!=null) b.prepareMedia(ps); return true; } protected Activity getActivity(){return this;} public abstract Activity.Panel getActivityPanel(PlayStation ps); public boolean helpSolutionAllowed(){ return false; } public boolean helpWindowAllowed(){ return helpWindow && ((helpSolutionAllowed() && showSolution) || helpMsg!=null); } public abstract int getMinNumActions(); public boolean mustPauseSequence(){ return getMinNumActions()!=0; } public boolean canReinit(){ return true; } public boolean hasInfo(){ return ((infoUrl!=null && infoUrl.length()>0) || (infoCmd!=null && infoCmd.length()>0)); } public boolean hasRandom(){ return false; } public boolean shuffleAlways(){ return false; } public boolean needsKeyboard(){ return false; } public void end(){ if(eventSounds!=null){ eventSounds.close(); eventSounds=null; } clear(); } public void clear(){ } @Override public void finalize() throws Throwable{ try { end(); } finally { super.finalize(); } } /** Getter for property windowSize. * @return Value of property windowSize. */ public Dimension getWindowSize() { return new Dimension(windowSize); } /** Setter for property windowSize. * @param windowSize New value of property windowSize. */ public void setWindowSize(Dimension windowSize) { this.windowSize = new Dimension(windowSize); } public abstract class Panel extends JPanel { public Skin skin=null; public boolean solved=false; public Image bgImage=null; protected boolean playing=false; protected boolean firstRun=true; protected int currentItem=0; protected BoxConnector bc=null; public PlayStation ps; /** Creates new ActivityPanel */ protected Panel(PlayStation ps) { super(); this.ps=ps; setMinimumSize(new Dimension(100, 100)); setPreferredSize(new Dimension(500, 400)); enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.KEY_EVENT_MASK); // 02-oct-2006: Added a call to initAutoContentProvider initAutoContentProvider(); } public Activity getActivity(){ return Activity.this; } public PlayStation getPs(){ return ps; } public void buildVisualComponents() throws Exception { playing=false; skin=null; if(skinFileName!=null && skinFileName.length()>0) skin=project.mediaBag.getSkinElement(skinFileName, ps); bgImage=null; if(bgImageFile!=null && bgImageFile.length()>0){ MediaBagElement mbe=project.mediaBag.getImageElement(bgImageFile); bgImage=mbe.getImage(); //bgImageFile=mbe.getFileName(); bgImageFile=mbe.getName(); } //ac.setBackgroundSettings(bgColor, image, tiledBgImg); setBackground(activityBgColor); if(transparentBg){ setOpaque(false); } setBorder(border ? BorderFactory.createBevelBorder(BevelBorder.RAISED) : BorderFactory.createEmptyBorder()); invalidate(); } protected void playEvent(int event){ if(eventSounds!=null) eventSounds.play(event); } public void initActivity() throws Exception{ if(playing){ playing=false; ps.reportEndActivity(Activity.this, solved); } solved=false; ps.reportNewActivity(Activity.this, 0); enableCounters(); //playEvent(EventSounds.START); } public void startActivity() throws Exception{ // do nothing } public void showHelp(){} public abstract void render(Graphics2D g2, Rectangle dirtyRegion); public abstract Dimension setDimension(Dimension maxSize); public void processMouse(MouseEvent e){} public void processKey(KeyEvent e){} public boolean isPlaying(){ return playing; } public final void fitTo(Rectangle proposed, Rectangle bounds){ Point origin=new Point(0,0); if(absolutePositioned && absolutePosition!=null){ //origin=new Point(absolutePosition); //origin.translate(fx, fy); origin.x=Math.max(0, absolutePosition.x+proposed.x); origin.y=Math.max(0, absolutePosition.y+proposed.y); proposed.width-=absolutePosition.x; proposed.height-=absolutePosition.y; } Dimension d=setDimension(new Dimension( Math.max(2*margin+Activity.MINIMUM_WIDTH, proposed.width), Math.max(2*margin+Activity.MINIMUM_HEIGHT, proposed.height))); if(!absolutePositioned){ origin.setLocation( Math.max(0, proposed.x+(proposed.width-d.width)/2), Math.max(0, proposed.y+(proposed.height-d.height)/2)); } if(origin.x+d.width>bounds.width) origin.x=Math.max(0, bounds.width-d.width); if(origin.y+d.height>bounds.height) origin.y=Math.max(0, bounds.height-d.height); setBounds(origin.x, origin.y, d.width, d.height); } @Override public void paintComponent(Graphics g){ Graphics2D g2=(Graphics2D)g; RenderingHints rh=g2.getRenderingHints(); g2.setRenderingHints(DEFAULT_RENDERING_HINTS); //RenderingHints rh=ac.setScreenRenderingHints(g2); if(!transparentBg){ if(activityBgGradient==null || activityBgGradient.hasTransparency()) super.paintComponent(g2); if(activityBgGradient!=null) activityBgGradient.paint(g2, new Rectangle(0, 0, getWidth(), getHeight())); } while(true){ BoxBase.flagFontReduced=false; render(g2, g2.getClipBounds()); if(!BoxBase.flagFontReduced) break; } g2.setRenderingHints(rh); } public void forceFinishActivity(){ } public void finishActivity(boolean result){ playing=false; solved=result; if(bc!=null) bc.end(); if(result){ setAndPlayMsg(END, EventSounds.FINISHED_OK); //if(messages[END]==null || messages[END].mediaContent==null) // playEvent(EventSounds.FINISHED_OK); //ps.setMsg(messages[END]); } else{ setAndPlayMsg(END_ERROR, EventSounds.FINISHED_ERROR); //if(messages[END_ERROR]==null || messages[END_ERROR].mediaContent==null) // playEvent(EventSounds.FINISHED_ERROR); //ps.setMsg(messages[END_ERROR]); } //ps.playMsg(); ps.activityFinished(solved); ps.reportEndActivity(Activity.this, solved); } protected void setAndPlayMsg(int msgCode, int eventSoundsCode){ ps.setMsg(messages[msgCode]); if(messages[msgCode]==null || messages[msgCode].mediaContent==null) playEvent(eventSoundsCode); else ps.playMsg(); } public void end(){ forceFinishActivity(); if(playing){ if(bc!=null) bc.end(); ps.reportEndActivity(Activity.this, solved); playing=false; solved=false; } clear(); } public abstract void clear(); @Override public void finalize() throws Throwable{ try { end(); } finally { super.finalize(); } } protected void enableCounters(){ enableCounters(bTimeCounter, bScoreCounter, bActionsCounter); } protected void enableCounters(boolean eTime, boolean eScore, boolean eActions){ ps.setCounterEnabled(TIME_COUNTER, eTime); if(countDownTime) ps.setCountDown(TIME_COUNTER, maxTime); ps.setCounterEnabled(SCORE_COUNTER, eScore); ps.setCounterEnabled(ACTIONS_COUNTER, eActions); if(countDownActions) ps.setCountDown(ACTIONS_COUNTER, maxActions); } @Override public void doLayout(){ } protected void shuffle(ActiveBoxBag[] bg, boolean visible, boolean fitInArea){ int steps=shuffles; //if(visible) ac.getComponent().paint(ac.getComponent().getGraphics()); int i=shuffles; while(i>0){ int k=i>steps ? steps:i; for(ActiveBoxBag abb : bg) if(abb!=null) abb.scrambleCells(k, fitInArea); //if(visible) ac.getComponent().paint(ac.getComponent().getGraphics()); i-=steps; } } @Override protected void processEvent(AWTEvent e){ if(playing && e instanceof MouseEvent){ if(e.getID()==MouseEvent.MOUSE_PRESSED && !hasFocus()) requestFocus(); // Modified 05-Aug-2010: // Medium button must not be processed when "fressa" enabled if(e.getID()!=MouseEvent.BUTTON3 || ps.getFressa()==null) processMouse((MouseEvent)e); } else if(playing && e instanceof KeyEvent) processKey((KeyEvent)e); super.processEvent(e); } public void calcScanPoints(){ if(ps.getFressa()!=null){ ps.getFressa().calcScanPoints(); } } public void calcInactivePoints(){ // does nothing by default } public void nextScanPosition() { if(ps.getFressa()!=null){ ps.getFressa().nextScanPosition(); } } public void clickLeftButton(){ if(ps.getFressa()!=null){ ps.getFressa().clickLeftButton(); if(ps.getFressa().jumpWellPlaced) calcInactivePoints(); } } } }