update author list in license for (JAL-826)
[jalview.git] / src / jalview / gui / WebserviceInfo.java
index ea2e166..9f8a8a8 100755 (executable)
@@ -1,20 +1,19 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
- * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
+ * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
  * 
- * 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 file is part of Jalview.
  * 
- * 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.
+ * Jalview 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 3 of the License, or (at your option) any later version.
  * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ * Jalview 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.
+ * 
+ * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  */
 package jalview.gui;
 
@@ -24,17 +23,25 @@ import java.awt.*;
 import java.awt.event.*;
 import java.awt.image.*;
 import javax.swing.*;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.event.HyperlinkEvent.EventType;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.StyleSheet;
 
+import jalview.bin.Cache;
 import jalview.jbgui.*;
 import jalview.ws.WSClientI;
 
 /**
- * Base class for web service client thread and gui
+ * Base class for web service client thread and gui TODO: create StAX parser to
+ * extract html body content reliably when preparing html formatted job statuses
  * 
  * @author $author$
  * @version $Revision$
  */
-public class WebserviceInfo extends GWebserviceInfo
+public class WebserviceInfo extends GWebserviceInfo implements
+        HyperlinkListener
 {
 
   /** Job is Queued */
@@ -77,19 +84,94 @@ public class WebserviceInfo extends GWebserviceInfo
 
   private boolean viewResultsImmediatly = true;
 
+  /**
+   * Get
+   * 
+   * @param flag
+   *          to indicate if results will be shown in a new window as soon as
+   *          they are available.
+   */
+  public boolean isViewResultsImmediatly()
+  {
+    return viewResultsImmediatly;
+  }
+
+  /**
+   * Set
+   * 
+   * @param flag
+   *          to indicate if results will be shown in a new window as soon as
+   *          they are available.
+   */
+  public void setViewResultsImmediatly(boolean viewResultsImmediatly)
+  {
+    this.viewResultsImmediatly = viewResultsImmediatly;
+  }
+
+  private StyleSheet getStyleSheet(HTMLEditorKit editorKit)
+  {
+
+    // Copied blatantly from
+    // http://www.velocityreviews.com/forums/t132265-string-into-htmldocument.html
+    StyleSheet myStyleSheet = new StyleSheet();
+
+    myStyleSheet.addStyleSheet(editorKit.getStyleSheet());
+
+    editorKit.setStyleSheet(myStyleSheet);
+
+    /*
+     * Set the style sheet rules here by reading them from the constants
+     * interface.
+     */
+    /*
+     * for (int ix=0; ix<CSS_RULES.length; ix++) {
+     * 
+     * myStyleSheet.addRule(CSS_RULES[ix]);
+     * 
+     * }
+     */
+    return myStyleSheet;
+
+  }
+
   // tabbed or not
   public synchronized int addJobPane()
   {
     JScrollPane jobpane = new JScrollPane();
-    JTextArea progressText = new JTextArea();
-    progressText.setFont(new java.awt.Font("Verdana", 0, 10));
-    progressText.setBorder(null);
-    progressText.setEditable(false);
-    progressText.setText("WS Job");
-    progressText.setLineWrap(true);
-    progressText.setWrapStyleWord(true);
+    JComponent _progressText;
+    if (renderAsHtml)
+    {
+      JEditorPane progressText = new JEditorPane("text/html", "");
+      progressText.addHyperlinkListener(this);
+      _progressText = progressText;
+      // progressText.setFont(new java.awt.Font("Verdana", 0, 10));
+      // progressText.setBorder(null);
+      progressText.setEditable(false);
+      /*
+       * HTMLEditorKit myEditorKit = new HTMLEditorKit();
+       * 
+       * StyleSheet myStyleSheet = getStyleSheet(myEditorKit);
+       * 
+       * HTMLDocument tipDocument = (HTMLDocument)
+       * (myEditorKit.createDefaultDocument());
+       * 
+       * progressText.setDocument(tipDocument);
+       */progressText.setText("<html><h1>WS Job</h1></html>");
+    }
+    else
+    {
+      JTextArea progressText = new JTextArea();
+      _progressText = progressText;
+
+      progressText.setFont(new java.awt.Font("Verdana", 0, 10));
+      progressText.setBorder(null);
+      progressText.setEditable(false);
+      progressText.setText("WS Job");
+      progressText.setLineWrap(true);
+      progressText.setWrapStyleWord(true);
+    }
     jobpane.setName("JobPane");
-    jobpane.getViewport().add(progressText, null);
+    jobpane.getViewport().add(_progressText, null);
     jobpane.setBorder(null);
     if (jobPanes == null)
     {
@@ -123,9 +205,9 @@ public class WebserviceInfo extends GWebserviceInfo
    * Creates a new WebserviceInfo object.
    * 
    * @param title
-   *                short name and job type
+   *          short name and job type
    * @param info
-   *                reference or other human readable description
+   *          reference or other human readable description
    */
   public WebserviceInfo(String title, String info)
   {
@@ -136,13 +218,13 @@ public class WebserviceInfo extends GWebserviceInfo
    * Creates a new WebserviceInfo object.
    * 
    * @param title
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    * @param info
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    * @param width
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    * @param height
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    */
   public WebserviceInfo(String title, String info, int width, int height)
   {
@@ -164,7 +246,7 @@ public class WebserviceInfo extends GWebserviceInfo
    * cancellable, whether the 'merge results' button is shown.
    * 
    * @param newservice
-   *                service client to query for capabilities
+   *          service client to query for capabilities
    */
   public void setthisService(jalview.ws.WSClientI newservice)
   {
@@ -196,13 +278,13 @@ public class WebserviceInfo extends GWebserviceInfo
    * DOCUMENT ME!
    * 
    * @param title
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    * @param info
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    * @param width
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    * @param height
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    */
   void init(String title, String info, int width, int height)
   {
@@ -233,20 +315,20 @@ public class WebserviceInfo extends GWebserviceInfo
     Thread thread = new Thread(ap);
     thread.start();
     final WebserviceInfo thisinfo = this;
-    frame
-            .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
-            {
-              public void internalFrameClosed(
-                      javax.swing.event.InternalFrameEvent evt)
-              {
-                // System.out.println("Shutting down webservice client");
-                WSClientI service = thisinfo.getthisService();
-                if (service != null && service.isCancellable())
-                {
-                  service.cancelJob();
-                }
-              };
-            });
+    frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
+    {
+      public void internalFrameClosed(
+              javax.swing.event.InternalFrameEvent evt)
+      {
+        // System.out.println("Shutting down webservice client");
+        WSClientI service = thisinfo.getthisService();
+        if (service != null && service.isCancellable())
+        {
+          service.cancelJob();
+        }
+      };
+    });
+    frame.validate();
 
   }
 
@@ -254,7 +336,7 @@ public class WebserviceInfo extends GWebserviceInfo
    * DOCUMENT ME!
    * 
    * @param status
-   *                integer status from state constants
+   *          integer status from state constants
    */
   public void setStatus(int status)
   {
@@ -313,7 +395,7 @@ public class WebserviceInfo extends GWebserviceInfo
    * DOCUMENT ME!
    * 
    * @param text
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    */
   public void setInfoText(String text)
   {
@@ -324,7 +406,7 @@ public class WebserviceInfo extends GWebserviceInfo
    * DOCUMENT ME!
    * 
    * @param text
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    */
   public void appendInfoText(String text)
   {
@@ -342,15 +424,23 @@ public class WebserviceInfo extends GWebserviceInfo
     {
       addJobPane();
     }
-    return ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
-            .getComponent(0)).getText();
+    if (renderAsHtml)
+    {
+      return ((JEditorPane) ((JScrollPane) jobPanes.get(which))
+              .getViewport().getComponent(0)).getText();
+    }
+    else
+    {
+      return ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
+              .getComponent(0)).getText();
+    }
   }
 
   /**
    * DOCUMENT ME!
    * 
    * @param text
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    */
   public void setProgressText(int which, String text)
   {
@@ -358,15 +448,99 @@ public class WebserviceInfo extends GWebserviceInfo
     {
       addJobPane();
     }
-    ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
-            .getComponent(0)).setText(text);
+    if (renderAsHtml)
+    {
+      ((JEditorPane) ((JScrollPane) jobPanes.get(which)).getViewport()
+              .getComponent(0)).setText(ensureHtmlTagged(text));
+    }
+    else
+    {
+      ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
+              .getComponent(0)).setText(text);
+    }
+  }
+
+  /**
+   * extract content from &lt;body&gt; content &lt;/body&gt;
+   * 
+   * @param text
+   * @param leaveFirst
+   *          - set to leave the initial html tag intact
+   * @param leaveLast
+   *          - set to leave the final html tag intact
+   * @return
+   */
+  private String getHtmlFragment(String text, boolean leaveFirst,
+          boolean leaveLast)
+  {
+    if (text == null)
+    {
+      return null;
+    }
+    String lowertxt = text.toLowerCase();
+    int htmlpos = leaveFirst ? -1 : lowertxt.indexOf("<body");
+
+    int htmlend = leaveLast ? -1 : lowertxt.indexOf("</body");
+    int htmlpose = lowertxt.indexOf(">", htmlpos), htmlende = lowertxt
+            .indexOf(">", htmlend);
+    if (htmlend == -1 && htmlpos == -1)
+    {
+      return text;
+    }
+    if (htmlend > -1)
+    {
+      return text.substring((htmlpos == -1 ? 0 : htmlpose + 1), htmlend);
+    }
+    return text.substring(htmlpos == -1 ? 0 : htmlpose + 1);
+  }
+
+  /**
+   * very simple routine for adding/ensuring html tags are present in text.
+   * 
+   * @param text
+   * @return properly html tag enclosed text
+   */
+  private String ensureHtmlTagged(String text)
+  {
+    if (text == null)
+    {
+      return "";
+    }
+    String lowertxt = text.toLowerCase();
+    int htmlpos = lowertxt.indexOf("<body");
+    int htmlend = lowertxt.indexOf("</body");
+    int doctype = lowertxt.indexOf("<!doctype");
+    int xmltype = lowertxt.indexOf("<?xml");
+    if (htmlend == -1)
+    {
+      text = text + "</body></html>";
+    }
+    if (htmlpos > -1)
+    {
+      if ((doctype > -1 && htmlpos > doctype)
+              || (xmltype > -1 && htmlpos > xmltype))
+      {
+        text = "<html><head></head><body>\n" + text.substring(htmlpos - 1);
+      }
+    }
+    else
+    {
+      text = "<html><head></head><body>\n" + text;
+    }
+    if (text.indexOf("<meta") > -1)
+    {
+      System.err.println("HTML COntent: \n" + text
+              + "<< END HTML CONTENT\n");
+
+    }
+    return text;
   }
 
   /**
    * DOCUMENT ME!
    * 
    * @param text
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    */
   public void appendProgressText(int which, String text)
   {
@@ -374,8 +548,21 @@ public class WebserviceInfo extends GWebserviceInfo
     {
       addJobPane();
     }
-    ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
-            .getComponent(0)).append(text);
+    if (renderAsHtml)
+    {
+      String txt = getHtmlFragment(
+              ((JEditorPane) ((JScrollPane) jobPanes.get(which))
+                      .getViewport().getComponent(0)).getText(), true,
+              false);
+      ((JEditorPane) ((JScrollPane) jobPanes.get(which)).getViewport()
+              .getComponent(0)).setText(ensureHtmlTagged(txt
+              + getHtmlFragment(text, false, true)));
+    }
+    else
+    {
+      ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
+              .getComponent(0)).append(text);
+    }
   }
 
   /**
@@ -406,7 +593,7 @@ public class WebserviceInfo extends GWebserviceInfo
    * get the tab title for a subjob
    * 
    * @param which
-   *                int
+   *          int
    * @return String
    */
   public String getProgressName(int which)
@@ -430,9 +617,9 @@ public class WebserviceInfo extends GWebserviceInfo
    * set the tab title for a subjob
    * 
    * @param name
-   *                String
+   *          String
    * @param which
-   *                int
+   *          int
    */
   public void setProgressName(String name, int which)
   {
@@ -451,7 +638,7 @@ public class WebserviceInfo extends GWebserviceInfo
    * Gui action for cancelling the current job, if possible.
    * 
    * @param e
-   *                DOCUMENT ME!
+   *          DOCUMENT ME!
    */
   protected void cancel_actionPerformed(ActionEvent e)
   {
@@ -461,9 +648,8 @@ public class WebserviceInfo extends GWebserviceInfo
       // anyhow - it has to stop threads and clean up
       // JBPNote : TODO: Instead of a warning, we should have an optional 'Are
       // you sure?' prompt
-      JOptionPane.showInternalMessageDialog(Desktop.desktop,
-              "This job cannot be cancelled.\nJust close the window.",
-              "Cancel job", JOptionPane.WARNING_MESSAGE);
+      warnUser("This job cannot be cancelled.\nJust close the window.",
+              "Cancel job");
     }
     else
     {
@@ -473,6 +659,26 @@ public class WebserviceInfo extends GWebserviceInfo
   }
 
   /**
+   * Spawns a thread that pops up a warning dialog box with the given message
+   * and title.
+   * 
+   * @param message
+   * @param title
+   */
+  public void warnUser(final String message, final String title)
+  {
+    javax.swing.SwingUtilities.invokeLater(new Runnable()
+    {
+      public void run()
+      {
+        JOptionPane.showInternalMessageDialog(Desktop.desktop, message,
+                title, JOptionPane.WARNING_MESSAGE);
+
+      }
+    });
+  }
+
+  /**
    * Set up GUI for user to get at results - and possibly automatically display
    * them if viewResultsImmediatly is set.
    */
@@ -606,4 +812,31 @@ public class WebserviceInfo extends GWebserviceInfo
       g1.drawImage(offscreen, 0, 0, this);
     }
   }
+
+  boolean renderAsHtml = false;
+
+  public void setRenderAsHtml(boolean b)
+  {
+    renderAsHtml = b;
+  }
+
+  public void hyperlinkUpdate(HyperlinkEvent e)
+  {
+    if (e.getEventType() == EventType.ACTIVATED)
+    {
+      String url=null;
+      try
+      {
+        url = e.getURL().toString();
+        Desktop.showUrl(url);
+      } catch (Exception x)
+      {
+        if (url!=null) { 
+          Cache.log.error("Couldn't handle string "+url+" as a URL.");
+        }
+        // ignore any exceptions due to dud links.
+      }
+
+    }
+  }
 }