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

  1. /*
  2.  * @(#)WRWindow.java    1.93 95/05/21 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 awt.*;
  23. import java.util.*;
  24. import java.io.*;
  25. import net.www.html.TagRef;
  26. import net.www.html.URL;
  27. import net.www.html.Tag;
  28.  
  29. /**
  30.  * A Window subclass that contains hotjava documents.
  31.  * html.Document objects are assigned to WRWindow's, which causes
  32.  * those documents to be displayed in the window.  WRWindow also
  33.  * serves as the focal point for handling document actions, such as
  34.  * following links, or moving up and down the document stack.
  35.  * @see        net.www.html.Document
  36.  * @see        browser.Document
  37.  * @see        Window
  38.  * @version 1.93, 21 May 1995
  39.  * @author  Jonathan Payne
  40.  */
  41.  
  42. public class WRWindow extends TextWindow {
  43.     /** Current document info. */
  44.     DocumentInfo    currentDocumentInfo;
  45.  
  46.     /** Last document, set before fetching a new one, in case fetch is
  47.         aborted and we want to restore. */
  48.     DocumentInfo    lastDocumentInfo;
  49.  
  50.     /** Current document (from document info), stored here so that
  51.     it cannot be reclaimed by the garbage collector. */
  52.     Object        currentContent;
  53.  
  54.     Stack        docStack = new Stack();
  55.     int            stackIndex = 0;
  56.     HistoryVector   docHistory = new HistoryVector();
  57.     DocumentManager manager = new DocumentManager(this);
  58.     Frame        frame;
  59.  
  60.     static boolean delayImageLoading = false;
  61.     static boolean delayAppletLoading = false;
  62.  
  63.     /**
  64.      * Constructs a new WRWindow as the named child of another
  65.      * Window.
  66.      */
  67.     public WRWindow(Window parent, String client) {
  68.     super(parent, client);
  69.     setSticky(false);
  70.     frame = parent.getFrame();
  71.     }
  72.  
  73.     /**
  74.      * Constructs a new WRWindow as the named child of a Frame.
  75.      */
  76.     public WRWindow(Frame parent, String client) {
  77.     super(parent, client);
  78.     setSticky(false);
  79.     frame = parent;
  80.     }
  81.  
  82.     /**
  83.      * Display a status message in the frame that contains this
  84.      * WRWindow.
  85.      */
  86.     public void status(String s) {
  87.     ((hotjava) parent).setMessage(s);
  88.     }
  89.  
  90.     /**
  91.      * Turn on or off delayed image loading.  If delayed image
  92.      * loading is turned on, and there are currently delayed images
  93.      * being fetched, they are flushed.  If delayed image loading is
  94.      * turned off, and there are images that need loading in the
  95.      * current document, they are fetched now.
  96.      */
  97.     public void setDelayImageLoading(boolean on) {
  98.     if (on != delayImageLoading) {
  99.         delayImageLoading = on;
  100.         if (!on) {
  101.         relayout();
  102.         }
  103.     }
  104.     }
  105.  
  106.     /**
  107.      * Turn on or off delayed applet loading.  If delayed applet
  108.      * loading is turned on, and there are currently delayed applets
  109.      * being fetched, they are flushed.  If delayed applet loading is
  110.      * turned off, and there are applets that need loading in the
  111.      * current document, they are feteched now.
  112.      */
  113.     public void setDelayAppletLoading(boolean on) {
  114.     if (on != delayAppletLoading) {
  115.         delayAppletLoading = on;
  116.         if (!delayAppletLoading) {
  117.         DisplayItem items[] = getItems();
  118.         // First load and initialize everyone
  119.         for (int i = 0 ; i < items.length ; i++) {
  120.             if ((items[i] != null) && (items[i] instanceof AppletDisplayItem)) {
  121.             AppletDisplayItem applet = (AppletDisplayItem)items[i];
  122.             applet.load();
  123.             applet.init();
  124.             }
  125.         }
  126.  
  127.         // reformat the window
  128.         relayout();
  129.  
  130.         // Now start them, they keep track of their state so it
  131.         // is ok to start an item twice.
  132.         for (int i = 0 ; i < items.length ; i++) {
  133.             if ((items[i] != null) && (items[i] instanceof AppletDisplayItem)) {
  134.             AppletDisplayItem applet = (AppletDisplayItem)items[i];
  135.             applet.start();
  136.             }
  137.         }
  138.         }
  139.     }
  140.     }
  141.  
  142.     /**
  143.      * Notice the completion of background image reading, and
  144.      * reformat the current document if there are no more pending
  145.      * images.
  146.      */
  147.     public void imageProgress(int pending) {
  148.     if (pending > 0) {
  149.         // status("Reading images: " + pending + " left to read ...");
  150.     } else if (currentDocumentInfo != null) {
  151.         relayout();
  152.             // status("");
  153.     }
  154.     }
  155.  
  156.     /**
  157.      * Find an intra-document reference as specified by the url
  158.      * parameter.
  159.      */
  160.     synchronized void findRef(URL url) {
  161.     Vector refs = ((Document) currentContent).getTags();
  162.     int cnt = refs.size();
  163.     int i = 0;
  164.     Tag anchorTag = Tag.lookup("a");
  165.     int pos = -1;
  166.  
  167.     while (--cnt >= 0) {
  168.         TagRef ref = (TagRef) refs.elementAt(i++);
  169.         String name;
  170.  
  171.         if (ref.tag != anchorTag || ref.isEnd)
  172.         continue;
  173.         if ((name = ref.getAttribute("name")) != null &&
  174.             name.equals(url.ref)) {
  175.         pos = ref.pos;
  176.         break;
  177.         }
  178.     }
  179.     if (pos >= 0) {
  180.         scrollToTextPosition(pos);
  181.     } else {
  182.         System.out.print("Cannot find name ref: #" + url.ref + "\n");
  183.         scrollAbsolute(0, 0);
  184.     }
  185.     }
  186.  
  187.     public synchronized void scrollToTextPosition(int pos) {
  188.     int i = count();
  189.  
  190.     while (--i >= 0) {
  191.         DisplayItem    di = nthItem(i);
  192.  
  193.         if (di instanceof WRTextItem) {
  194.         WRTextItem    wi = (WRTextItem) di;
  195.  
  196.         if ((wi.offset > 0) && (wi.offset <= pos)) {
  197.             scrollAbsolute(0, wi.y);
  198.             return;
  199.         }
  200.         }
  201.     }
  202.     scrollAbsolute(0, 0);
  203.     }
  204.  
  205.     boolean isHtmlDocument(Object content) {
  206.     return (content != null && content instanceof Document);
  207.     }
  208.  
  209.     protected void stopApplets() {
  210.     if (isHtmlDocument(currentContent)) {
  211.         ((Document) currentContent).stopApplets();
  212.     }
  213.     }
  214.  
  215.     protected void startApplets() {
  216.     if (isHtmlDocument(currentContent)) {
  217.         ((Document) currentContent).startApplets();
  218.     }
  219.     }
  220.  
  221.     protected void destroyApplets() {
  222.     if (isHtmlDocument(currentContent)) {
  223.         ((Document) currentContent).destroyApplets();
  224.     }
  225.     }
  226.  
  227.     protected void flushImages() {
  228.     if (isHtmlDocument(currentContent)) {
  229.         ((Document) currentContent).flushImages();
  230.     }
  231.     }
  232.  
  233.     /**
  234.      * Set the current document displayed by this window to the
  235.      * specified DocumentInfo.  If this document has been visited
  236.      * previously, this scrolls to the last position at which this
  237.      * document was displayed.  If the document contains a #ref,
  238.      * that is also handled here.
  239.      * @see DocumentInfo
  240.      * @see net.www.html.URL
  241.      */
  242.     protected boolean setDocument(DocumentInfo newDoc, boolean restore) {
  243.     boolean    resident = newDoc.isResident();
  244.  
  245.     if (!resident) {
  246.         status("Fetching " + newDoc.url.toExternalForm() + " ...");
  247.         disablePointerMotionEvents();
  248.     }
  249.     Object    content = newDoc.getContent();
  250.  
  251.     if (!resident) {
  252.         manager.cacheDocument(newDoc);
  253.         enablePointerMotionEvents();
  254.     }
  255.  
  256.     if (content == null || content instanceof net.www.html.MimeLauncher) {
  257.         newDoc.setCacheable(false);
  258.         unpushDocument(newDoc);
  259.         status("Done");
  260.         return false;
  261.     } else if (content instanceof InputStream) {
  262.         /* A stream that the user might want saved */
  263.         new ContentSaver((InputStream) content, frame,
  264.                  newDoc.url, this);
  265.         content = null;
  266.         newDoc.setCacheable(false);
  267.         unpushDocument(newDoc);
  268.         status("Done");
  269.         return false;
  270.     } else if (content instanceof DIBitmap) {
  271.         content = new ImageDisplayItem(createImage((DIBitmap) content));
  272.     }
  273.  
  274.     /* stop applets of current document, if current document is
  275.        actually an html page */
  276.     stopApplets();
  277.  
  278.     /* set the url field in the main window */
  279.     ((hotjava) parent).setURL(newDoc.url.toExternalForm());
  280.  
  281.     /* close the current document */
  282.     if (currentDocumentInfo != null) {
  283.         currentDocumentInfo.close(this);
  284.     }
  285.  
  286.     /* set the title, if there is one */
  287.     ((hotjava) parent).setTitle(newDoc.getTitle());
  288.  
  289.     Object    previousContent = currentContent;
  290.  
  291.     currentDocumentInfo = newDoc;
  292.     currentContent = content;
  293.  
  294.     if (!(content instanceof Document)) {
  295.         //setText(null);
  296.         invalidate();
  297.         if (restore) {
  298.         setScrolling(0, newDoc.scrollY <= 0 ? newDoc.scrollY : 0);
  299.         } else {
  300.         setScrolling(0, 0);
  301.         }
  302.         paint();
  303.     } else {
  304.         if (currentDocumentInfo == null || content != previousContent) {
  305.         invalidate();
  306.         //setText(((Document) content).getText());
  307.         if (!preserveScrollingAtNextValidate) {
  308.             setScrolling(0, 0);
  309.         }
  310.         }
  311.         if (!restore && newDoc.url.ref != null) {
  312.         /* Unfortunately, this causes a paint in one place
  313.            followed by a scroll to another */
  314.         validate();
  315.         findRef(newDoc.url);
  316.         } else {
  317.         if (!valid) {
  318.             if (newDoc.scrollY <= 0) {
  319.             setScrolling(0, newDoc.scrollY);
  320.             }
  321.             validate();
  322.         } else {
  323.             scrollAbsolute(0, -newDoc.scrollY);
  324.         }
  325.         }
  326.         startApplets();
  327.     }
  328.     return true;
  329.     }
  330.  
  331.     /**
  332.      * Returns the current document being displayed in this Window.
  333.      */
  334.     public Document document() {
  335.     return isHtmlDocument(currentContent) ? (Document) currentContent : null;
  336.     }
  337.  
  338.     synchronized public void pushURL(URL url) {
  339.     if (stackIndex < docStack.size()) {
  340.         docStack.setSize(stackIndex);
  341.     }
  342.  
  343.     DocumentInfo    info = manager.newDocument(url);
  344.  
  345.     if (!info.emptyDocument)
  346.         pushDocument(info);
  347.     setDocumentInNewThread(info, false, true);
  348.     }
  349.  
  350.     public void fetching(String msg) {
  351.     status(msg);
  352.     }
  353.  
  354.     public void setButtonStates() {
  355.     ((hotjava)parent).toolBar.setButtonStates(stackIndex, docStack);
  356.     }
  357.  
  358.     public void allowStop(boolean yes) {
  359.     // HACK: allow stop anytime.  Fix to disable button later.
  360. //    ((hotjava)parent).toolBar.allowStop(yes);
  361.     ((hotjava)parent).toolBar.allowStop(true);
  362.     }
  363.  
  364.     /**
  365.      * Deals with the fact that some document that was being fetched
  366.      * in the background has now completed.  In general, this pushes
  367.      * this document into the document stack, making it the new
  368.      * current document.
  369.      */
  370.     void pushDocument(DocumentInfo info) {
  371.     if (info != null) {
  372.         docStack.push(info);
  373.         stackIndex++;
  374.     }
  375.     setButtonStates();
  376.     }
  377.  
  378.     /**
  379.      * Deals with the fact that some document may have been pushed on
  380.      * the document stack before we knew what it was.  If it turned
  381.      * out to be uncacheable, then we must remove it.
  382.      */
  383.     void unpushDocument(DocumentInfo info) {
  384.     if (info != null) {
  385.         if (docStack.elementAt(stackIndex - 1) == info) {
  386.         stackIndex--;
  387.         setButtonStates();
  388.         }
  389.     }
  390.     }
  391.  
  392.     synchronized void addToHistory(DocumentInfo info) {
  393.     HistoryItem item = new HistoryItem(info.url, info.getTitle());
  394.     docHistory.pushElement(item);
  395.     ((hotjava)parent).histWindow.newElement();
  396.     hotjava.history.addUrl(info.url);
  397.     }
  398.  
  399.     /**
  400.      * Handles the callback from pressing the reload button in the main
  401.      * window.
  402.      */
  403.     public void reload() {
  404.     if (currentDocumentInfo != null) {
  405.         uncacheCurrentDocument();
  406.         preserveScrollingAtNextValidate = true;
  407.         setDocumentInNewThread(currentDocumentInfo, true, false);
  408.     }
  409.     }
  410.  
  411.     /**
  412.      * Handles the callback from pressing the Back button in the main
  413.      * window.
  414.      */
  415.     public void backup() {
  416.     if (stackIndex > 1) {
  417.         stackIndex--;
  418.         setDocumentInNewThread((DocumentInfo)docStack.elementAt(stackIndex-1),
  419.                    true, false);
  420.     } 
  421.     setButtonStates();
  422.     }
  423.  
  424.     /**
  425.      * Handles the callback from pressing the Forward button in the main
  426.      * window.
  427.      */
  428.     public void forward() {
  429.     if (stackIndex < docStack.size()) {
  430.         setDocumentInNewThread((DocumentInfo) docStack.elementAt(stackIndex++),
  431.                    true, false);
  432.     }
  433.     setButtonStates();
  434.     }
  435.  
  436.     DocumentSwitcher switcherThread = null;
  437.  
  438.     /**
  439.      * Go get another document; run the fetch in a separate thread.
  440.      */
  441.  
  442.     synchronized void setDocumentInNewThread(DocumentInfo info,
  443.                          boolean restore,
  444.                          boolean push) {
  445.  
  446.     lastDocumentInfo = currentDocumentInfo;
  447.     if (switcherThread != null && switcherThread.isAlive()) {
  448.         switcherThread.stop();
  449.     }
  450.     
  451.     switcherThread = new DocumentSwitcher(this, info, restore, push);
  452.     switcherThread.start();
  453.     }
  454.  
  455.     public void dumpStack(String msg) {
  456.     PrintStream o = System.out;
  457.     int size = docStack.size();
  458.     o.println(msg+" size = "+size+"; index = "+stackIndex);
  459.     for (int i = 0; i < size; i++) {
  460.         if (i == stackIndex - 1) {
  461.         o.print("--> ");
  462.         } else {
  463.         o.print("    ");
  464.         }
  465.         o.println("["+i+"] "+((DocumentInfo)(docStack.elementAt(i))).url.toExternalForm());
  466.     }
  467.     }
  468.  
  469.     /**
  470.      * Stop the document fetch now in progress.  Invoked by the stop button.
  471.      * Try to restore as much of the previous state as possible.
  472.      */
  473.     
  474.     public synchronized void stopFetch() {
  475.  
  476.     // If something's being fetched, annihilate the fetcher and restore
  477.     // things to their rightful order.
  478.  
  479.     // Try to stop any image fetches
  480.     ImageCache.stopFetch();
  481.         
  482.     if (switcherThread != null && switcherThread.isAlive()) {
  483.         DocumentInfo docBeingFetched = switcherThread.fetchingDoc();
  484.         switcherThread.stop();
  485.  
  486.         // Deal with the case in which we caught the fetch after the doc
  487.         // arrived, but before it got completely displayed.  Restore the
  488.         // previous document.
  489.         /*
  490.         if (docBeingFetched != null && stackIndex > 0) {
  491.         unpushDocument(docBeingFetched);
  492.         if (lastDocumentInfo != null) {
  493.             setDocument(lastDocumentInfo, true);
  494.         }
  495.         }
  496.         */
  497.         
  498.         status("Stopped.");
  499.  
  500.         // Allow highlighting of links again
  501.         enablePointerMotionEvents();
  502.  
  503.         // Set the state of the UI appropriately
  504.         setButtonStates();
  505.         allowStop(false);
  506.  
  507.     }
  508.     }
  509.     
  510.     private boolean preserveScrollingAtNextValidate = false;
  511.  
  512.     public void uncacheCurrentDocument() {
  513.     destroyApplets();
  514.     flushImages();
  515.     URL.flushClassLoader();
  516.     currentContent = null;
  517.  
  518.     DocumentInfo    di = currentDocumentInfo;
  519.  
  520.     if (di != null) {
  521.         di.clearDoc();
  522.     }
  523.     }
  524.  
  525.     /**
  526.      * Relays out the current document, trying to preserve the current
  527.      * position in the document.
  528.      */
  529.     public void relayout() {
  530.     addUpdateRequest(new RelayoutUpdateRequest());
  531.     }
  532.  
  533.     public void preserveScrolling() {
  534.     preserveScrollingAtNextValidate = true;
  535.     }
  536.  
  537.     public void handleResize() {
  538.     preserveScrollingAtNextValidate = true;
  539.     super.handleResize();
  540.     relayout();
  541.     }
  542.  
  543.     public void validate() {
  544.     if (valid) {
  545.         return;
  546.     }
  547.     if (isHtmlDocument(currentContent)) {
  548.         status("Formatting ...");
  549.         setFormatter(new WRFormatter(this, (Document) currentContent));
  550.         layoutDocument();
  551.         valid = true;
  552.         preserveScrollingAtNextValidate = false;
  553.         status("Complete");
  554.     } else if (currentContent != null
  555.            && currentContent instanceof DisplayItem) {
  556.         DisplayItem    di = (DisplayItem) currentContent;
  557.         int        x = (width-di.width)/2;
  558.         int        y = (height-di.height)/2;
  559.  
  560.         clearItems();
  561.         startNewLine(0, y);
  562.         addItem(di);
  563.         di.move(x, y);
  564.         logicalHeight = di.height;
  565.     }
  566.     updateScrollbar();
  567.     }
  568. }
  569.  
  570. class ContentSaver extends Thread {
  571.     InputStream is;
  572.     URL u;
  573.     Frame f;
  574.     WRWindow status_target;
  575.  
  576.     private void status(String s) {
  577.     if (status_target != null && s != null)
  578.         status_target.status(s);
  579.     }
  580.  
  581.     ContentSaver(InputStream IS, Frame F, URL U, WRWindow st) {
  582.     is = IS;
  583.     u = U;
  584.     f = F;
  585.     status_target = st;
  586.     setName("ContentSaver");
  587.     start();
  588.     }
  589.  
  590.     public void run() {
  591.     FileDialog fd = new FileDialog("Save to file", f);
  592.     String dfn = "file.out";
  593.     String ofn = null;
  594.     if (u != null && u.file != null) {
  595.         dfn = u.file;
  596.         int i = dfn.lastIndexOf('/');
  597.         if (i < 0)
  598.         i = dfn.lastIndexOf(':');
  599.         if (i > 0)
  600.         dfn = dfn.substring(i + 1);
  601.     }
  602.     OutputStream os = null;
  603.     while (os == null) {
  604.         ofn = fd.chooseFile(dfn);
  605.         if (ofn == null)
  606.         break;
  607.         try {
  608.         os = new FileOutputStream(ofn);
  609.         } catch(Exception e) {
  610.         status("Can't open file for write: " + ofn);
  611.         os = null;
  612.         }
  613.         
  614.     }
  615.     if (os != null) {
  616.         status("Writing "+ofn);
  617.         try {
  618.         byte buf[] = new byte[2048];
  619.         int i;
  620.         while ((i = is.read(buf)) >= 0) {
  621.             os.write(buf, 0, i);
  622.         }
  623.         status("Finished writing "+ofn);
  624.         } catch(Exception e) {
  625.         status("Save to file failed: " + e);
  626.         }
  627.     }
  628.     if (os != null)
  629.         os.close();
  630.     if (is != null)
  631.         is.close();
  632.     fd.dispose();
  633.     }
  634. }
  635.  
  636.  
  637. class RelayoutUpdateRequest extends DIWUpdateRequest {
  638.     void execute(DisplayItemWindow w) {
  639.     WRWindow    wr = (WRWindow) w;
  640.  
  641.     wr.invalidate();
  642.     wr.preserveScrolling();
  643.     wr.validate();
  644.     }
  645. }
  646.  
  647. class DocumentSwitcher extends Thread {
  648.     WRWindow        owner;
  649.     DocumentInfo    info;
  650.     boolean        restore;
  651.     boolean        push;
  652.  
  653.     DocumentSwitcher(WRWindow owner, DocumentInfo info,
  654.              boolean restore, boolean push) {
  655.     this.owner = owner;
  656.     this.info = info;
  657.     this.restore = restore;
  658.     this.push = push;
  659.     }
  660.  
  661.     public DocumentInfo fetchingDoc() {
  662.     return info;
  663.     }
  664.  
  665.     public void run() {
  666.     setName("DocumentSwitcher");
  667.     try {
  668.         setPriority(Thread.MIN_PRIORITY+2);
  669.  
  670.         owner.allowStop(true);
  671.         owner.setDocument(info, restore);
  672.         owner.allowStop(false);
  673.  
  674.         ((hotjava)(owner.parent)).pageHasSource(owner.document() != null);
  675.  
  676.         if (push) {
  677.         /* Add to history AFTER the document is fetched, because
  678.            only then do we know the title of the document.  If we
  679.            do this beforehand, the act of asking for the title
  680.            will cause the document to be fetched. */
  681.         owner.addToHistory(info);
  682.         }
  683.  
  684.         owner.setButtonStates();
  685.         owner.enablePointerMotionEvents();
  686.  
  687.         owner.switcherThread = null;
  688.     } catch (ThreadDeath d) {
  689.         /*
  690.          * Try to stop a document's applets.  We use check()
  691.          * directly to avoid having the DocRef fetch the
  692.          * document if it isn't currently in memory.
  693.          */
  694.         Object o;
  695.         
  696.         try {
  697.         o = owner.currentDocumentInfo.doc.check();
  698.         } catch (NullPointerException e) {
  699.         // Some of the fields may not have been set yet.
  700.         o = null;
  701.         }
  702.         if (o != null) {
  703.         Document doc;
  704.  
  705.         try {
  706.             doc = (Document) o;
  707.             doc.stopApplets();
  708.         } catch (ClassCastException e) {
  709.             // doc was not a Document; just press on.
  710.         }
  711.         }
  712.     }
  713.     }
  714. }
  715.