// ====================================================================
// Downloaded from the de.caff's Applet problems workaround page:
//	http://www.caff.de/workarounds/
//
// It may be used and copied freely as you like. 
// USE YOUR OWN RISK!
//
// Created by rammi@caff.de.
// ====================================================================

import java.applet.*;
import java.awt.*;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;
import java.util.Enumeration;
import java.util.Vector;

/**
 *  This Applet shows some workarounds for common problems
 *  in Applets.
 *  See <a href="http://www.caff.de/workarounds/index.html#common_printing">
 *  de&middot;'s Applet problems workarounds page</a> for a discription of the
 *  problems addressed in this code.
 *
 *  @author <a href="mailto:rammi@caff.de">Rammi</a>
 */
public class PrintTest
  extends Applet
{
  /** Are we still in initial state? */
  /* private */ boolean initial = true;

  /** Whether to print our own stuff or use the standard. */
  /* private */ boolean standardPrint = true;

  /** Whether to work around MSIE's printing on new Applet bug? */
  /* private */ boolean avoidMsieNewBug = false;

  /** Whether to initialize fonts before printing. */
  /* private */ boolean initFont = false;

  /** Whether to initialize the clip area to something useful if it's not set? */
  /* private */ boolean initClip = false;

  /** Whether to scale Netscapes clip area to 75%? */
  /* private */ boolean scaleNetscape75 = false;

  // Checkboxes:
  private Checkbox takeOld;
  private Checkbox font;
  private Checkbox clip;
  private Checkbox p75;

  /**
   *  Initialize the Applet. 
   *  Sets up the view.
   */
  public void init()
  {
    setLayout(new FlowLayout(5, 5, 5));

    setBackground(Color.lightGray);  // necessary for Netscape

    Checkbox std = new Checkbox("Use the default printing (paint Applet directly)",
                               standardPrint);

    takeOld = new Checkbox("Avoid MSIE bug which prints always a new uninitialized Applet",
                           avoidMsieNewBug);

    font = new Checkbox("Initialize font explicitely before printing",
                        initFont);

    clip = new Checkbox("Initialize clip area if not set", initClip);

    p75 = new Checkbox("Scale clip area to 75%", scaleNetscape75);


    // === Create callbacks ===
    std.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent event)
      {
        standardPrint = (event.getStateChange() == ItemEvent.SELECTED);
        initial = false; // something has happened

        stdEnable();
      }
    });

    takeOld.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent event)
      {
        avoidMsieNewBug = (event.getStateChange() == ItemEvent.SELECTED);
        initial = false; // something has happened
      }
    });

    font.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent event)
      {
        initFont = (event.getStateChange() == ItemEvent.SELECTED);
        initial = false; // something has happened
      }
    });

    clip.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent event)
      {
        initClip = (event.getStateChange() == ItemEvent.SELECTED);
        initial = false; // something has happened

        p75.setEnabled(initClip);
      }
    });

    p75.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent event)
      {
        scaleNetscape75 = (event.getStateChange() == ItemEvent.SELECTED);
        initial = false; // something has happened
      }
    });


    // === add everything ===
    add(std);
    add(takeOld);
    add(font);
    add(clip);
    add(p75);

  }

  /**
   *  Start the applet.
   */
  public void start()
  {
    stdEnable();
  }

  /**
   *  Enable everything according to settings.
   */
  private void stdEnable()
  {
    // === initialize ===
    takeOld.setEnabled(!standardPrint);
    font.setEnabled(!standardPrint);
    clip.setEnabled(!standardPrint);
    p75.setEnabled(!standardPrint && initClip);
  }

  /**
   *  Print everything. Depending on the <code>standardGraphics</code>
   *  this uses the default or our own stuff.
   *  @param g the graphics context
   */
  public void printAll(Graphics g)
  {
    // usually the next if is not necessary, but in case of the MSIE bug
    // standardPrint for the second Applet created is always true.
    // Usually you wouldn't have such a variable at all so the test would always
    // happen in print()
    if (tryToAvoidMsieBug(g)) {
      return;
    }
    if (standardPrint) {
      super.printAll(g);
    }
    else {
      // usually just use print, but not in this special case
      // print(g);
      // the following is a shortcut because there's no need to test twice in this example
      realPrint(g, getSettings());
    }
  }

  /**
   *  Print this applet. Depending on the <code>standardGraphics</code>
   *  this uses the default or our own stuff.
   *  Some browsers call this as entry method for printing.
   *  @param g the graphics context
   */
  public void print(Graphics g)
  {
    if (tryToAvoidMsieBug(g)) {
      return;
    }

    if (standardPrint) {
      super.print(g);
    }
    else {
      // === End of workaround ===
      realPrint(g, getSettings());

    }
  }

  /**
   *  Internal method. Try to workaround the braindamaged bug where MSIE
   *  creates a new Applet and calls print() on that instead on the applet
   *  in the page.
   *  @param  g  graphics context
   *  @return <code>true</code> the workaround was used,
   *          <code>false</code> otherwise
   */
  private boolean tryToAvoidMsieBug(Graphics g)
  {
    // === special workaround for braindamaged IE which creates a ===
    // === new applet from scratch for printing                   ===
    try {
      String vendor = System.getProperty("java.vendor");
      System.out.println("vendor="+vendor);
      if (vendor != null  &&  vendor.indexOf("Microsoft") != -1) {
        for (Enumeration e = getAppletContext().getApplets();  e.hasMoreElements();  ) {
          Applet applet = (Applet)e.nextElement();
          System.out.println("Applet "+applet+(applet.isShowing() ? " showing" : "NOT showing"));
          if (applet.isShowing()) {
            if (applet instanceof PrintTest) {
              PrintTest other = (PrintTest)applet;

              // cannot test our own settings!
              if (!other.standardPrint  &&  other.avoidMsieNewBug) {
                Vector output = other.getSettings();
                output.addElement("*** Used MSIE new applet print workaround ***");
                // the other applet should do the printing:
                other.realPrint(g, output);
                return true;
              }
            }
          }
        }
      }
    } catch (Throwable e) {
      // nothing
    }

    return false;
  }

  /**
   *  Print in our own way. Instead of printing everything like it's shown on the web page
   *  we draw a rectangle with the Applet's size in some comments.
   *  @param g  graphics context
   *  @param lines  info lines so far
   */
  private void realPrint(Graphics g, Vector lines)
  {
    Rectangle bounds = g.getClipBounds();
    if (bounds == null  &&  initClip) {
      // Netscape problem workaround
      bounds = getBounds();
      if (scaleNetscape75) {
        String vendor = System.getProperty("java.vendor");
        if (vendor != null  &&  vendor.startsWith("Netscape")) {
          lines.addElement("*** Using Netscape 75% scaling workround ***");
          // correct bad Netscape scaling
          bounds.width = 3*bounds.width/4;
          bounds.height = 3*bounds.height/4;
        }
      }
      g.setClip(bounds);
    }
    lines.addElement("Applet bounds: "+bounds);

    if (bounds != null) {
      // clear the bounds
      g.setColor(Color.white);
      g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
      g.setColor(Color.black);
      g.drawRect(bounds.x, bounds.y, bounds.width-1, bounds.height-1);
    }

    final int border = 2;

    if (g.getFont() == null  &&  initFont) {
      Font sans = new Font("SansSerif", Font.PLAIN, 12);
      g.setFont(sans);
    }

    try {
      int line = 0;
      FontMetrics metrics = g.getFontMetrics();
      for (Enumeration e = lines.elements();  e.hasMoreElements();  ) {
        g.drawString(e.nextElement().toString(),
                     bounds.x+border,
                     bounds.y+metrics.getMaxAscent()+border+line*metrics.getHeight());
        ++line;
      }
    } catch (NullPointerException x) {
      showStatus("Printing failed due to NullPointerException (probably no font is set)");
      x.printStackTrace();
    }

   }

  /**
   *  Get the current settings as a vector of strings.
   *  @return settings as string vector
   */
  private Vector getSettings()
  {
    Vector v = new Vector();

    v.addElement("User has changed something: "+!initial);
    v.addElement("Trying to workaround MSIE new Applet bug: "+avoidMsieNewBug);
    v.addElement("Initializing font if unset: "+initFont);
    v.addElement("Initializing clip area if unset: "+initClip);
    if (initClip) {
      v.addElement("Scaling clip area to 75% size for Netscape: "+scaleNetscape75);
    }

    return v;
  }

  /**
   *  Set enabled. Overwritten because browser uses this, too.
   *  @param state new state
   */
  public void setEnabled(boolean state)
  {
    super.setEnabled(state);
    if (state) {
      stdEnable();
    }
  }
}

