home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1995 November / PCWK1195.iso / inne / win95 / sieciowe / hotja32.lzh / hotjava / classsrc / browser / wrformatter.java < prev    next >
Text File  |  1995-08-11  |  10KB  |  359 lines

  1. /*
  2.  * @(#)WRFormatter.java    1.35 95/03/20 Jonathan Payne
  3.  *
  4.  * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * Permission to use, copy, modify, and distribute this software
  7.  * and its documentation for NON-COMMERCIAL purposes and without
  8.  * fee is hereby granted provided that this copyright notice
  9.  * appears in all copies. Please refer to the file "copyright.html"
  10.  * for further important copyright and licensing information.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  13.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  14.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  15.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  16.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  17.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  18.  */
  19.  
  20. package browser;
  21.  
  22. import java.io.*;
  23. import awt.*;
  24. import java.util.*;
  25. import net.www.html.TagRef;
  26.  
  27. /**
  28.  * Class WRFormatter adds the functionality above and beyond that of
  29.  * a basic formatter to layout html documents.  This includes
  30.  * handling of html anchors, html list contexts, html forms, html
  31.  * image alignment
  32.  * @version 1.35, 20 Mar 1995
  33.  * @author Jonathan Payne
  34.  * @author James Gosling
  35.  */
  36. public
  37. class WRFormatter extends Formatter {
  38.     final boolean   debug = false;
  39.  
  40.     /** This is the document we're formatting. */
  41.     Document    doc;
  42.  
  43.     /** Theoretically anchors should not nest, but just in case,
  44.     the current anchor context is stored in this stack.  When
  45.     display items are created, they check the current anchor
  46.     stack and "do the right thing" if they are in an anchor
  47.     context. */
  48.     Stack    anchorStack = new Stack();
  49.  
  50.     /** This is the list stack.  <li> <dd> and <dt> all check their
  51.     list "context" to figure out the proper margin setting, and
  52.     look to the list context to generate the next bullet, etc. */
  53.     Stack    listStack = new Stack();
  54.  
  55.     /** This is the current form we're defining.  There can
  56.     only be one form at a time.  It is an error to have
  57.     nested forms.  But considering how stupid mosaic is,
  58.     I better allow for multiple forms JUST IN CASE.  So
  59.     this is actually a stack. */
  60.     Stack    formStack = new Stack();
  61.  
  62.     protected int getLength() {
  63.     return doc.getText().length;
  64.     }
  65.  
  66.     protected int charAt(int i) {
  67.     return doc.getText()[i] & 0xFF;
  68.     }
  69.  
  70.     /** Makes a hotjava text item. */
  71.     protected TextDisplayItem makeTextItem(int pos0, int pos1) {
  72.     TagRef    ac = anchorContext();
  73.  
  74.     return new WRTextItem(win, doc.getText(), pos0, pos1, ac);
  75.     }
  76.  
  77.     /** Pushes a new anchor onto the anchor stack. */
  78.     void pushAnchor(TagRef a) {
  79.     anchorStack.push(a);
  80.     }
  81.  
  82.     /** Pops an anchor from the anchor stack. */
  83.     TagRef popAnchor() {
  84.     return (TagRef) anchorStack.pop();
  85.     }
  86.  
  87.     /**
  88.      * Returns the current anchor context, or null if there is no
  89.      * anchor.
  90.      */
  91.     TagRef anchorContext() {
  92.     return anchorStack.size() > 0 ? (TagRef) anchorStack.peek() : null;
  93.     }
  94.  
  95.     /** Pushes a new list onto the list stack. */
  96.     void pushList(TagRef a) {
  97.     listStack.push(a);
  98.     }
  99.  
  100.     /**
  101.      * Pops a new list onto the list stack.
  102.      */
  103.     void popList() {
  104.     try {
  105.         listStack.pop();
  106.     } catch (EmptyStackException e) {
  107.         System.out.print("List stack mess up\n");
  108.     }
  109.     }
  110.  
  111.     /**
  112.      * Returns the current list context, or null if there is none.
  113.      */
  114.     WRListRef listContext() {
  115.     return (listStack.size() > 0) ? (WRListRef) listStack.peek() : null;
  116.     }
  117.  
  118.     /**
  119.      * Pushes a new Form onto the form stack.  Technically there
  120.      * should only ever be one form on the form stack at a time, but
  121.      * knowing Mosaic anything is possible!
  122.      */
  123.     void pushForm(FormTagRef form) {
  124.     formStack.push(form);
  125.     }
  126.  
  127.     /** Pops a form from the form stack. */
  128.     void popForm() {
  129.     formStack.pop();
  130.     }
  131.  
  132.     /**
  133.      * Returns the current form context, or null if there is none.
  134.      */
  135.     FormTagRef formContext() {
  136.     return (formStack.size() > 0) ? (FormTagRef) formStack.peek() : null;
  137.     }
  138.  
  139.     /**
  140.      * This adjusts a DisplayItem on the current line.  It's called
  141.      * for each display item on a line, when the formatter finishes
  142.      * the line, to go back and adjust items.  E.g., this handles
  143.      * lining up baselines for all text that appeared on the line,
  144.      * or, image alignment ala html and mosaic.
  145.      */
  146.     protected void adjustItem(DisplayItem di, int x) {
  147.     if (di instanceof Alignable) {
  148.         Alignable    a = (Alignable) di;
  149.         int        align;
  150.  
  151.         if ((align = a.getAlign()) != Alignable.A_NONE) {
  152.         align = a.getAlign();
  153.         }
  154.         int    lineHeight = lineDescent + lineAscent;
  155.  
  156.         switch (align) {
  157.         case Alignable.A_TOP:
  158.         di.move(x, di.y);
  159.         break;        /* already there */
  160.  
  161.         case Alignable.A_TEXTTOP:
  162.         di.move(x, y + lineAscent - fontAscent);
  163.         break;
  164.  
  165.         case Alignable.A_ABSMIDDLE:
  166.         di.move(x, y + (lineHeight - di.height) / 2);
  167.         break;
  168.  
  169.         case Alignable.A_MIDDLE:
  170.         di.move(x, y + lineAscent - di.height / 2);
  171.         break;
  172.  
  173.         case Alignable.A_BASELINE:
  174.         case Alignable.A_BOTTOM:
  175.         di.move(x, y + lineAscent - di.height);
  176.         break;
  177.  
  178.         case Alignable.A_ABSBOTTOM:
  179.         di.move(x, y + lineHeight - di.height);
  180.         break;
  181.         }
  182.     } else {
  183.         super.adjustItem(di, x);
  184.     }
  185.     }
  186.  
  187.     /** This is to get around the motif bug which doesn't allow
  188.     motif widgets to initially appear partially in the window
  189.     (off the top).  Don't ask me why! */
  190.     static final int    motifWidgetOffset = 0;
  191.     final boolean    alignFirstLine = true;
  192.     int            windowScrollY;
  193.  
  194.     protected void finishCurrentLine() {
  195.     int start = lineStartIndex;
  196.     int cnt = itemCount - start;
  197.     int topY = y;
  198.     int adjustedY = y + windowScrollY;
  199.     int h = lineAscent + lineDescent;
  200.  
  201.     /* If we're aligning the first line so that it's not partially
  202.        visible off the top, and y WAS < 0 but now is > 0, then
  203.        adjust scrollY so that this line we're displaying now is not
  204.        negative. */
  205.     if (alignFirstLine && adjustedY < motifWidgetOffset
  206.         && adjustedY + h > 0) {
  207.         win.setScrolling(0, -(topY - motifWidgetOffset));
  208.     }
  209.  
  210.     super.finishCurrentLine();
  211.  
  212.     while (--cnt >= 0) {
  213.         win.updateChild(win.nthItem(start++), true);
  214.     }
  215.     }
  216.  
  217.     /**
  218.      * Adds a display item to the window.  This takes into
  219.      * consideration image alignment when adjusting the lineAscent
  220.      * and lineDescent instance variables.  This doesn't worry about
  221.      * positioning everything correctly at this point - that is
  222.      * handled by adjustItem, which is called when the entire line
  223.      * is layed out.
  224.      */
  225.  
  226.     public boolean addDisplayItem(DisplayItem di, boolean checkForWrap) {
  227.     int oldAscent = lineAscent;
  228.     int oldDescent = lineDescent;
  229.  
  230.     if (!super.addDisplayItem(di, checkForWrap)) {
  231.         return false;
  232.     }
  233.  
  234.     if (di instanceof Alignable) {
  235.         Alignable    wri = (Alignable)di;
  236.  
  237.         /* First restore these.  The super class really doesn't
  238.            know what to do with anything other than bottom image
  239.            alignment, and that can screw us up, so we start with
  240.            a clean slate. */
  241.         lineAscent = oldAscent;
  242.         lineDescent = oldDescent;
  243.  
  244.         int    align = wri.getAlign();
  245.         if (align == Alignable.A_NONE) {
  246.         align = Alignable.A_BOTTOM;
  247.         }
  248.  
  249.         /* Now check to see if this image and the current
  250.            image alignment changes the line ascent or descent.
  251.  
  252.            NOTE: If no text has occurred on the line, and we
  253.            come across a TEXTTOP or TOP image, lineDescent is
  254.            adjusted to the height of the image.  This is because
  255.            lineAscent and fontAscent are 0 at the time. I don't
  256.            know how to fix this yet. */
  257.  
  258.         switch (align) {
  259.         case Alignable.A_TEXTTOP:
  260.         lineDescent = Math.max(lineDescent, di.height - fontAscent);
  261.         break;
  262.  
  263.         case Alignable.A_TOP:
  264.         lineDescent = Math.max(lineDescent, di.height - lineAscent);
  265.         break;
  266.  
  267.         case Alignable.A_ABSMIDDLE:
  268.         {
  269.             int    lineHeight = lineAscent + lineDescent;
  270.         }
  271.         /* falls into */
  272.             
  273.  
  274.         case Alignable.A_MIDDLE:
  275.         {
  276.             int    halfHeight = di.height / 2;
  277.  
  278.             lineAscent = Math.max(lineAscent, halfHeight);
  279.             lineDescent = Math.max(lineDescent, halfHeight);
  280.             break;
  281.         }
  282.  
  283.         case Alignable.A_BASELINE:
  284.         case Alignable.A_BOTTOM:
  285.         lineAscent = Math.max(lineAscent, di.height);
  286.         break;
  287.  
  288.         case Alignable.A_ABSBOTTOM:
  289.         lineAscent = Math.max(lineAscent, di.height - lineDescent);
  290.         break;
  291.         }
  292.     }
  293.     return true;
  294.     }        
  295.  
  296.     protected Vector    tagList;
  297.     protected int    tagIndex;
  298.  
  299.     protected int processStyleRefs(int pos) {
  300.     WRTagRef    ref = null;
  301.     int        tagLimit = tagList.size();
  302.  
  303.     while (tagIndex < tagLimit) {
  304.         ref = (WRTagRef) tagList.elementAt(tagIndex);
  305.         if (ref.pos > pos) {
  306.         break;
  307.         }
  308.         if (debug) {
  309.         System.out.println("Processing ref #" + tagIndex + "[pos = " + pos + "] = " + ref);
  310.         }
  311.         ref.apply(this);
  312.         tagIndex += 1;
  313.     }
  314.     if (tagIndex < tagLimit) {
  315.         return ref.pos;
  316.     } else {
  317.         return doc.getText().length;
  318.     }
  319.     }
  320.  
  321.     protected void reset() {
  322.     super.reset();
  323.     y = 20;
  324.     windowScrollY = win.getScrollY();
  325.     }
  326.  
  327.     public void layout() {
  328.     Document    doc = ((WRWindow) win).document();
  329.  
  330.     xlimit = win.width;
  331.     if (xlimit < 20) {
  332.         return;
  333.     }
  334.     anchorStack.setSize(0);
  335.     listStack.setSize(0);
  336.  
  337.     tagList = doc.getTags();
  338.     tagIndex = 0;
  339.  
  340.     /* now, walk through all the tag refs, and start fetching images */
  341.     int i = 0;
  342.     int cnt = tagList.size();
  343.     while (--cnt >= 0) {
  344.         TagRef  ref = (TagRef) tagList.elementAt(i++);
  345.  
  346.         if (ref instanceof ImgTagRef) {
  347.         ((ImgTagRef) ref).prime((WRWindow) win);
  348.         }
  349.     }
  350.  
  351.     super.layout();
  352.     }
  353.  
  354.     public WRFormatter(WRWindow m, Document d) {
  355.     super(m);
  356.     doc = d;
  357.     }
  358. }
  359.