// Copyright 1996, Marimba Inc. All Rights Reserved.


// @(#)Presentation.java, 1.63, 11/20/96





package marimba.gui;





import java.io.*;


import java.awt.*;


import java.net.*;


import java.util.*;


import java.applet.AudioClip;


import sun.applet.AppletAudioClip;





import marimba.io.*;


import marimba.persist.*;





/**


 * This class represents a presentation. A presentation is a


 * top level widget that can be saved to a file. It takes care


 * of the administration of external resources.


 *


 * @author	Arthur van Hoff


 * @version 	1.63, 11/20/96


 */


public class Presentation extends GroupWidget {


    public static boolean error;


    static Hashtable audioHash = new Hashtable();


    static Hashtable baseHash = new Hashtable();





    final static Color  defaultSelForeground = Color.white;


    final static Color  defaultSelBackground = new Color(0, 0, 124);





    /**


     * The possible options for the mouse cursor.


     * @see #getCursorOptions


     * @see #cursor


     */


    public static Options cursorOptions = new Options();


    static {


	cursorOptions.add("default", Frame.DEFAULT_CURSOR);


	cursorOptions.add("cross", Frame.CROSSHAIR_CURSOR);


	cursorOptions.add("text", Frame.TEXT_CURSOR);


	cursorOptions.add("wait", Frame.WAIT_CURSOR);


	cursorOptions.add("hand", Frame.HAND_CURSOR);


	cursorOptions.add("move", Frame.MOVE_CURSOR);


    }





    /**


     * The base of this presentation. Relative pathnames will


     * start with this base.


     * @see #getBase


     * @see #setBase


     */


    public URL  base;





    /**


     * The title of this presentation, displayed in the


     * title bar when this presentation is run.


     * @see #getTitle


     * @see #setTitle


     */


    public String  title;





    /**


     * The name of the file containing the icon for this


     * presentation.


     * @see #getIcon


     * @see #setIcon


     */


    public String  icon;





    /**


     * The mouse cursor for this presentation.


     * The constants for the cursor are defined in java.awt.Frame.


     * @see #getDefaultCursor


     * @see #setDefaultCursor


     * @see #resetCursor


     * @see #cursorOptions


     */


    public int defaultCursor;





    /**


     * The current mouse cursor for this presentation.


     * @see #getCursor


     * @see #setCursor


     * @see #defaultCursor


     */


    public int cursor;





    /**


     * Determines whether this presentation can be resized.


     * @see #isResizable


     * @see #setResizable


     */


    public boolean  resizable = true;





    /**


     * The foreground color for selections.


     * @see #getSelForeground


     * @see #setSelForeground


     * @see #setSelColors


     */


    public Color  selForeground = defaultSelForeground;





    /**


     * The background color for selections.


     * @see #getSelBackground


     * @see #setSelBackground


     * @see #setSelColors


     */


    public Color  selBackground = defaultSelBackground;





    /**


     * The class loader for this presentation.


     * @see #getURLClassLoader


     * @see #setBase


     */


    public URLClassLoader  loader;








    /**


     * Constructor.


     */


    public Presentation() {


	visible = false;


	lineMode = NONE;


	//wFont = defaultFont;


	//wForeground = defaultForeground;


	//wBackground = defaultBackground;


	north = east = south = west = 0;


    }





    /**


     * Construct a new presentation.


     */


    public Presentation(URL base) {


	this();


	setBase(base);


	resize(400, 300);


    }





    /**


     * Get properties.


     */


    public void getProperties(PropertyList list) {


	super.getProperties(list);


	list.setBoolean("resizable", resizable, true);


	list.setString("icon", icon, null);


	list.setString("title", title, null);


	list.setColor("selFore", selForeground, defaultSelForeground);


	list.setColor("selBack", selBackground, defaultSelBackground);


	list.setOption("cursor", cursorOptions, defaultCursor, Frame.DEFAULT_CURSOR);


    }





    /**


     * Set the properties.


     */


    public void setProperties(PropertyList list) {


	super.setProperties(list);


	resizable = list.getBoolean("resizable", true);


	icon = list.getString("icon", null);


	title = list.getString("title", null);


	selForeground = list.getColor("selFore", defaultSelForeground);


	selBackground = list.getColor("selBack", defaultSelBackground);


	defaultCursor = list.getOption("cursor", cursorOptions, Frame.DEFAULT_CURSOR);


	resetCursor();


	north = east = south = west = 0;


    }





    /**


     * Set foreground selection color.


     * @see #selForeground


     */


    public void setSelForeground(Color selForeground) {


	this.selForeground = selForeground;


    }





    /**


     * Set background selection color.


     * @see #selBackground


     */


    public void setSelBackground(Color selBackground) {


	this.selBackground = selBackground;


    }





    /**


     * Set both selection colors at once.


     * @see #selForeground


     * @see #selBackground


     */


    public void setSelColors(Color selForeground, Color selBackGround) {


	setSelForeground(selForeground);


	setSelBackground(selBackground);


    }





    /**


     * Get foreground selection color.


     * @see #selForeground


     */


    public Color getSelForeground() {


	return selForeground;


    }





    /**


     * Get background selection color.


     * @see #selBackground


     */


    public Color getSelBackground() {


	return selBackground;


    }





    /**


     * Make sure to set the right presentation in the


     * player if this one is replaced.


     */


    public void replace(Widget newWidget) {


	PlayerPanel player = getPlayerPanel();


	if ((player != null) && (player.presentation == this)) {


	    player.replacePresentation((Presentation)newWidget);


	}


    }





    /**


     * Get the presentation.


     */


    public Presentation getPresentation() {


	return this;


    }





    /**


     * Get the class loader for this presentation. This


     * may return null.


     * @see #loader


     */


    public URLClassLoader getURLClassLoader() {


	return loader;


    }





    /**


     * Get the base of this presentation


     * @see #base


     */


    public URL getBase() {


	return base;


    }





    /**


     * Set the base of this presentation, the loader is set to the


     * current class loader from the threadgroup.


     * @see #base


     * @see #loader


     */


    public void setBase(URL base) {


	URLClassLoader loader = ClassLoaderThreadGroup.getCurrentURLClassLoader();


	try {


	    loader = new URLClassLoader(loader, base);


	} catch (SecurityException e) {


	    // We must be running in a browser.


	    // Ignore the exception


	}


	setBase(base, loader);


    }





    /**


     * Set the base and class loader of this presentation


     * @see #base


     * @see #loader


     */


    public void setBase(URL base, URLClassLoader loader) {


	this.base = base;


	this.loader = loader;


    }





    /**


     * Get the title of this presentation.


     * @see #title


     */


    public String getTitle() {


	return title;


    }





    /**


     * Set the title of this presentation. This will change the


     * title of the window if this presentation is embedded in a


     * PlayerPanel who's parent is a Frame.


     * @see #title


     */


    public void setTitle(String title) {


	if (title != null && !title.equals(this.title)) {


	    this.title = title;





	    // Set the title in the parent window (if any)


	    PlayerPanel panel = getPlayerPanel();


	    if ((panel != null) && (panel.getPresentation() == this)) {


		Component parent = panel.getParent();


		if (parent instanceof Frame) {


		    ((Frame)parent).setTitle(title);


		}


	    }


	}


    }





    /**


     * Get the icon.


     * @see #icon


     */


    public String getIcon() {


	return icon;


    }





    /**


     * Set the icon.


     * @see #icon


     */


    public void setIcon(String icon) {


	this.icon = icon;


    }





    /**


     * Get the possible options for the mouse cursor.


     * @see #cursorOptions


     */


    public Options getCursorOptions() {


	return cursorOptions;


    }





    /**


     * Get the default mouse cursor.


     * @see #defaultCursor


     */


    public int getDefaultCursor() {


	return defaultCursor;


    }





    /**


     * Set the default mouse cursor.


     * @see #defaultCursor


     */


    public void setDefaultCursor(int defaultCursor) {


	this.defaultCursor = defaultCursor;


    }





    /**


     * Get the current mouse cursor.


     * @see #cursor


     */


    public int getCursor() {


	return cursor;


    }





    /**


     * Set the current mouse cursor.


     * @see #cursor


     */


    public void setCursor(int cursor) {


	this.cursor = cursor;


	for (Container c = getPlayerPanel() ; c != null ; c = c.getParent()) {


	    if (c instanceof Frame) {


		((Frame)c).setCursor(cursor);


		break;


	    }


	}


    }





    /**


     * Reset the current cursor to the default one.


     * @see #defaultCursor


     * @see #cursor


     */


    public void resetCursor() {


	setCursor(defaultCursor);


    }





    /**


     * Check whether this presentation is resizable.


     * @see #resizable


     */


    public boolean isResizable() {


	return resizable;


    }





    /**


     * Enable/disable resizing this presentation.


     * @see #resizable


     */


    public void setResizable(boolean resizable) {


	this.resizable = resizable;


    }





    /**


     * Add a base URL. This lets you refer to a resource using


     * the ~name/foo notation.


     */


    public static void addBase(String name, URL url) {


	if (url != null) {


	    baseHash.put(name.toLowerCase(), url);


	}


    }





    /**


     * Add a base URL. This lets you refer to a resource using


     * the ~name/foo notation.


     */


    public static void addBase(String name, String str) {


	if (str != null) {


	    try {


		addBase(name, new URL(str));


	    } catch (MalformedURLException e) {


		e.printStackTrace();


	    }


	}


    }





    /**


     * Construct the absolute URL for a resource.


     */


    public static URL getURL(URL base, String url) {


	try {


	    if (url.startsWith("~/")) {


		return new URL("file:" + System.getProperty("user.dir") +


			       url.substring(1).replace('/', File.separatorChar));


	    }


	    if (url.startsWith("~")) {


		int i = url.indexOf('/');


		if (i > 0) {


		    String nm = url.substring(1, i).toLowerCase();


		    URL baseURL = (URL)baseHash.get(nm);


		    if (baseURL != null) {


			return new URL(baseURL, url.substring(i+1));


		    }


		    String str = System.getProperty("marimba." + nm);


		    if (str != null) {


			addBase(nm, baseURL = new URL(str));


			return new URL(baseURL, url.substring(i+1));


		    }


		}


	    }





	    return new URL(base, url);


	} catch (MalformedURLException e) {


	    e.printStackTrace();


	}


	return null;


    }





    /**


     * Construct the absolute URL for a resource.


     */


    public URL getURL(String url) {


	return getURL(base, url);


    }





    /**


     * Get an image.


     */


    public Image getImage(String src) {


	URL url = getURL(base, src);


	return (url == null) ? null : ImageCache.get(url);


    }





    /**


     * Get an audio clip.


     */


    public AudioClip getAudioClip(String src) {


	URL  url = getURL(base, src);


	AudioClip  clip =  getAudioClipAt(url);


	return clip;


    }





    /**


     * Get an audio clip using a url.


     */


    public AudioClip getAudioClipAt(URL url) {


	if (url == null) {


	    return null;


	}


	// security manager check


	// System.getSecurityManager().checkConnect(url.getHost(), url.getPort());


	AudioClip clip = (AudioClip)audioHash.get(url);


	if (clip == null) {


	    audioHash.put(url, clip = new AppletAudioClip(url));


	}


	return clip;


    }





    /**


     * Find a widget in this presentation.


     */


    public Widget findWidget(String nm) {


	Widget w = super.findWidget(nm);


	if ((w == null) && nm.equals("presentation")) {


	    return this;


	}


	return w;


    }





    /**


     * Create an instance of a class in the context of this


     * presentation. This means that the class can be located


     * in the same area as the presentation.


     */


    public synchronized Object newInstance(String nm) throws ClassNotFoundException, IllegalAccessException, InstantiationException {


	return ((loader != null) ? loader.loadClass(nm) : Class.forName(nm)).newInstance();


    }





    /**


     * Get a presentation from a url.


     */


    public synchronized static Presentation getPresentation(URL url) {


	error = false;


	try {


	    FastInputStream s = new FastInputStream(url.openStream());


	    try {


		BinaryPersistentState state = new BinaryPersistentState();


		try {


		    state.setClassLoader(new URLClassLoader(state.getClassLoader(), url));


		} catch (SecurityException e) {


		    // We must be running in a browser.


		}





		Presentation p = (Presentation)state.loadObject(s);


		p.base = url;


		p.loader = state.getClassLoader();


		error = state.error;


		return p;


	    } finally {


		s.close();


	    }


	} catch (SyntaxError e) {


	    error = true;


	    e.printStackTrace();


	} catch (IOException e) {


	    error = true;


	    e.printStackTrace();


	}





	// There was some problem reading the file, try the backup


	if (!"file".equals(url.getProtocol())) {


	    return null;


	}





	String fileName = url.getFile();





	// See if there was a backup file


	File bakFile = new File(fileName + ".bak");


	if (!bakFile.exists()) {


	    return null;


	}





	// Make the bad file junk


	File file = new File(fileName);


	File junkFile = new File(fileName + ".junk");


	junkFile.delete();


	if (!file.renameTo(junkFile)) {


	    return null;


	}





	// Rename the backup file to the official file


	if (!bakFile.renameTo(file)) {


	    junkFile.renameTo(file);


	    return null;


	}





	// Try again


	// System.out.println("using backup presentation: " + bakFile);


	return getPresentation(url);


    }





    /**


     * Get a presentation from a file


     */


    public static Presentation getPresentation(String file) {


	try {


	    return getPresentation(new URL("file:" + file.replace(File.separatorChar, '/')));


	} catch (MalformedURLException e) {


	    e.printStackTrace();


	}


	return null;


    }





    /**


     * For debugging only.


     */


    public void paramString(StringBuffer buf) {


	super.paramString(buf);


	if (base != null) {


	    buf.append(",base=");


	    buf.append(base.toExternalForm());


	}


	if (title != null) {


	    buf.append(",title=");


	    buf.append(title);


	}


	if (icon != null) {


	    buf.append(",icon=");


	    buf.append(icon);


	}


	if (resizable) {


	    buf.append(",resizable");


	}


    }





    public boolean handleEvent(Event evt) {


	return super.handleEvent(evt);


    }


}


