c44e041811d0fd1f9ca15bee27c21369dcbfa2f3
[jalview.git] / src / jalview / gui / WebserviceInfo.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.gui;
19
20 import java.util.*;
21
22 import java.awt.*;
23 import java.awt.event.*;
24 import java.awt.image.*;
25 import javax.swing.*;
26 import javax.swing.event.HyperlinkEvent;
27 import javax.swing.event.HyperlinkListener;
28 import javax.swing.text.html.HTMLEditorKit;
29 import javax.swing.text.html.StyleSheet;
30
31 import jalview.jbgui.*;
32 import jalview.ws.WSClientI;
33
34 /**
35  * Base class for web service client thread and gui TODO: create StAX parser to
36  * extract html body content reliably when preparing html formatted job statuses
37  * 
38  * @author $author$
39  * @version $Revision$
40  */
41 public class WebserviceInfo extends GWebserviceInfo implements
42         HyperlinkListener, IProgressIndicator
43 {
44
45   /** Job is Queued */
46   public static final int STATE_QUEUING = 0;
47
48   /** Job is Running */
49   public static final int STATE_RUNNING = 1;
50
51   /** Job has finished with no errors */
52   public static final int STATE_STOPPED_OK = 2;
53
54   /** Job has been cancelled with no errors */
55   public static final int STATE_CANCELLED_OK = 3;
56
57   /** job has stopped because of some error */
58   public static final int STATE_STOPPED_ERROR = 4;
59
60   /** job has failed because of some unavoidable service interruption */
61   public static final int STATE_STOPPED_SERVERERROR = 5;
62
63   int currentStatus = STATE_QUEUING;
64
65   Image image;
66
67   int angle = 0;
68
69   String title = "";
70
71   jalview.ws.WSClientI thisService;
72
73   boolean serviceIsCancellable;
74
75   JInternalFrame frame;
76
77   JTabbedPane subjobs = null;
78
79   java.util.Vector jobPanes = null;
80
81   private boolean serviceCanMergeResults = false;
82
83   private boolean viewResultsImmediatly = true;
84
85   /**
86    * Get
87    * 
88    * @param flag
89    *          to indicate if results will be shown in a new window as soon as
90    *          they are available.
91    */
92   public boolean isViewResultsImmediatly()
93   {
94     return viewResultsImmediatly;
95   }
96
97   /**
98    * Set
99    * 
100    * @param flag
101    *          to indicate if results will be shown in a new window as soon as
102    *          they are available.
103    */
104   public void setViewResultsImmediatly(boolean viewResultsImmediatly)
105   {
106     this.viewResultsImmediatly = viewResultsImmediatly;
107   }
108
109   private StyleSheet getStyleSheet(HTMLEditorKit editorKit)
110   {
111
112     // Copied blatantly from
113     // http://www.velocityreviews.com/forums/t132265-string-into-htmldocument.html
114     StyleSheet myStyleSheet = new StyleSheet();
115
116     myStyleSheet.addStyleSheet(editorKit.getStyleSheet());
117
118     editorKit.setStyleSheet(myStyleSheet);
119
120     /*
121      * Set the style sheet rules here by reading them from the constants
122      * interface.
123      */
124     /*
125      * for (int ix=0; ix<CSS_RULES.length; ix++) {
126      * 
127      * myStyleSheet.addRule(CSS_RULES[ix]);
128      * 
129      * }
130      */
131     return myStyleSheet;
132
133   }
134
135   // tabbed or not
136   public synchronized int addJobPane()
137   {
138     JScrollPane jobpane = new JScrollPane();
139     JComponent _progressText;
140     if (renderAsHtml)
141     {
142       JEditorPane progressText = new JEditorPane("text/html", "");
143       progressText.addHyperlinkListener(this);
144       _progressText = progressText;
145       // progressText.setFont(new java.awt.Font("Verdana", 0, 10));
146       // progressText.setBorder(null);
147       progressText.setEditable(false);
148       /*
149        * HTMLEditorKit myEditorKit = new HTMLEditorKit();
150        * 
151        * StyleSheet myStyleSheet = getStyleSheet(myEditorKit);
152        * 
153        * HTMLDocument tipDocument = (HTMLDocument)
154        * (myEditorKit.createDefaultDocument());
155        * 
156        * progressText.setDocument(tipDocument);
157        */progressText.setText("<html><h1>WS Job</h1></html>");
158     }
159     else
160     {
161       JTextArea progressText = new JTextArea();
162       _progressText = progressText;
163
164       progressText.setFont(new java.awt.Font("Verdana", 0, 10));
165       progressText.setBorder(null);
166       progressText.setEditable(false);
167       progressText.setText("WS Job");
168       progressText.setLineWrap(true);
169       progressText.setWrapStyleWord(true);
170     }
171     jobpane.setName("JobPane");
172     jobpane.getViewport().add(_progressText, null);
173     jobpane.setBorder(null);
174     if (jobPanes == null)
175     {
176       jobPanes = new Vector();
177     }
178     int newpane = jobPanes.size();
179     jobPanes.add(jobpane);
180
181     if (newpane == 0)
182     {
183       this.add(jobpane, BorderLayout.CENTER);
184     }
185     else
186     {
187       if (newpane == 1)
188       {
189         // revert to a tabbed pane.
190         JScrollPane firstpane;
191         this.remove(firstpane = (JScrollPane) jobPanes.get(0));
192         subjobs = new JTabbedPane();
193         this.add(subjobs, BorderLayout.CENTER);
194         subjobs.add(firstpane);
195         subjobs.setTitleAt(0, firstpane.getName());
196       }
197       subjobs.add(jobpane);
198     }
199     return newpane; // index for accessor methods below
200   }
201
202   /**
203    * Creates a new WebserviceInfo object.
204    * 
205    * @param title
206    *          short name and job type
207    * @param info
208    *          reference or other human readable description
209    */
210   public WebserviceInfo(String title, String info)
211   {
212     init(title, info, 520, 500);
213   }
214
215   /**
216    * Creates a new WebserviceInfo object.
217    * 
218    * @param title
219    *          DOCUMENT ME!
220    * @param info
221    *          DOCUMENT ME!
222    * @param width
223    *          DOCUMENT ME!
224    * @param height
225    *          DOCUMENT ME!
226    */
227   public WebserviceInfo(String title, String info, int width, int height)
228   {
229     init(title, info, width, height);
230   }
231
232   /**
233    * DOCUMENT ME!
234    * 
235    * @return DOCUMENT ME!
236    */
237   public jalview.ws.WSClientI getthisService()
238   {
239     return thisService;
240   }
241
242   /**
243    * Update state of GUI based on client capabilities (like whether the job is
244    * cancellable, whether the 'merge results' button is shown.
245    * 
246    * @param newservice
247    *          service client to query for capabilities
248    */
249   public void setthisService(jalview.ws.WSClientI newservice)
250   {
251     thisService = newservice;
252     serviceIsCancellable = newservice.isCancellable();
253     frame.setClosable(!serviceIsCancellable);
254     serviceCanMergeResults = newservice.canMergeResults();
255     rebuildButtonPanel();
256   }
257
258   private void rebuildButtonPanel()
259   {
260     if (buttonPanel != null)
261     {
262       buttonPanel.removeAll();
263       if (serviceIsCancellable)
264       {
265         buttonPanel.add(cancel);
266         frame.setClosable(false);
267       }
268       else
269       {
270         frame.setClosable(true);
271       }
272     }
273   }
274
275   /**
276    * DOCUMENT ME!
277    * 
278    * @param title
279    *          DOCUMENT ME!
280    * @param info
281    *          DOCUMENT ME!
282    * @param width
283    *          DOCUMENT ME!
284    * @param height
285    *          DOCUMENT ME!
286    */
287   void init(String title, String info, int width, int height)
288   {
289     frame = new JInternalFrame();
290     frame.setContentPane(this);
291     Desktop.addInternalFrame(frame, title, width, height);
292     frame.setClosable(false);
293
294     this.title = title;
295     setInfoText(info);
296
297     java.net.URL url = getClass().getResource(
298             "/images/Jalview_Logo_small.png");
299     image = java.awt.Toolkit.getDefaultToolkit().createImage(url);
300
301     MediaTracker mt = new MediaTracker(this);
302     mt.addImage(image, 0);
303
304     try
305     {
306       mt.waitForID(0);
307     } catch (Exception ex)
308     {
309     }
310
311     AnimatedPanel ap = new AnimatedPanel();
312     titlePanel.add(ap, BorderLayout.CENTER);
313
314     Thread thread = new Thread(ap);
315     thread.start();
316     final WebserviceInfo thisinfo = this;
317     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
318     {
319       public void internalFrameClosed(
320               javax.swing.event.InternalFrameEvent evt)
321       {
322         // System.out.println("Shutting down webservice client");
323         WSClientI service = thisinfo.getthisService();
324         if (service != null && service.isCancellable())
325         {
326           service.cancelJob();
327         }
328       };
329     });
330     frame.validate();
331
332   }
333
334   /**
335    * DOCUMENT ME!
336    * 
337    * @param status
338    *          integer status from state constants
339    */
340   public void setStatus(int status)
341   {
342     currentStatus = status;
343   }
344
345   /**
346    * subjob status indicator
347    * 
348    * @param jobpane
349    * @param status
350    */
351   public void setStatus(int jobpane, int status)
352   {
353     if (jobpane < 0 || jobpane >= jobPanes.size())
354     {
355       throw new Error("setStatus called for non-existent job pane."
356               + jobpane);
357     }
358     switch (status)
359     {
360     case STATE_QUEUING:
361       setProgressName(jobpane + " - QUEUED", jobpane);
362       break;
363     case STATE_RUNNING:
364       setProgressName(jobpane + " - RUNNING", jobpane);
365       break;
366     case STATE_STOPPED_OK:
367       setProgressName(jobpane + " - FINISHED", jobpane);
368       break;
369     case STATE_CANCELLED_OK:
370       setProgressName(jobpane + " - CANCELLED", jobpane);
371       break;
372     case STATE_STOPPED_ERROR:
373       setProgressName(jobpane + " - BROKEN", jobpane);
374       break;
375     case STATE_STOPPED_SERVERERROR:
376       setProgressName(jobpane + " - ALERT", jobpane);
377       break;
378     default:
379       setProgressName(jobpane + " - UNKNOWN STATE", jobpane);
380     }
381   }
382
383   /**
384    * DOCUMENT ME!
385    * 
386    * @return DOCUMENT ME!
387    */
388   public String getInfoText()
389   {
390     return infoText.getText();
391   }
392
393   /**
394    * DOCUMENT ME!
395    * 
396    * @param text
397    *          DOCUMENT ME!
398    */
399   public void setInfoText(String text)
400   {
401     infoText.setText(text);
402   }
403
404   /**
405    * DOCUMENT ME!
406    * 
407    * @param text
408    *          DOCUMENT ME!
409    */
410   public void appendInfoText(String text)
411   {
412     infoText.append(text);
413   }
414
415   /**
416    * DOCUMENT ME!
417    * 
418    * @return DOCUMENT ME!
419    */
420   public String getProgressText(int which)
421   {
422     if (jobPanes == null)
423     {
424       addJobPane();
425     }
426     if (renderAsHtml)
427     {
428       return ((JEditorPane) ((JScrollPane) jobPanes.get(which))
429               .getViewport().getComponent(0)).getText();
430     }
431     else
432     {
433       return ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
434               .getComponent(0)).getText();
435     }
436   }
437
438   /**
439    * DOCUMENT ME!
440    * 
441    * @param text
442    *          DOCUMENT ME!
443    */
444   public void setProgressText(int which, String text)
445   {
446     if (jobPanes == null)
447     {
448       addJobPane();
449     }
450     if (renderAsHtml)
451     {
452       ((JEditorPane) ((JScrollPane) jobPanes.get(which)).getViewport()
453               .getComponent(0)).setText(ensureHtmlTagged(text));
454     }
455     else
456     {
457       ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
458               .getComponent(0)).setText(text);
459     }
460   }
461
462   /**
463    * extract content from &lt;body&gt; content &lt;/body&gt;
464    * 
465    * @param text
466    * @param leaveFirst
467    *          - set to leave the initial html tag intact
468    * @param leaveLast
469    *          - set to leave the final html tag intact
470    * @return
471    */
472   private String getHtmlFragment(String text, boolean leaveFirst,
473           boolean leaveLast)
474   {
475     if (text == null)
476     {
477       return null;
478     }
479     String lowertxt = text.toLowerCase();
480     int htmlpos = leaveFirst ? -1 : lowertxt.indexOf("<body");
481
482     int htmlend = leaveLast ? -1 : lowertxt.indexOf("</body");
483     int htmlpose = lowertxt.indexOf(">", htmlpos), htmlende = lowertxt
484             .indexOf(">", htmlend);
485     if (htmlend == -1 && htmlpos == -1)
486     {
487       return text;
488     }
489     if (htmlend > -1)
490     {
491       return text.substring((htmlpos == -1 ? 0 : htmlpose + 1), htmlend);
492     }
493     return text.substring(htmlpos == -1 ? 0 : htmlpose + 1);
494   }
495
496   /**
497    * very simple routine for adding/ensuring html tags are present in text.
498    * 
499    * @param text
500    * @return properly html tag enclosed text
501    */
502   private String ensureHtmlTagged(String text)
503   {
504     if (text == null)
505     {
506       return "";
507     }
508     String lowertxt = text.toLowerCase();
509     int htmlpos = lowertxt.indexOf("<body");
510     int htmlend = lowertxt.indexOf("</body");
511     int doctype = lowertxt.indexOf("<!doctype");
512     int xmltype = lowertxt.indexOf("<?xml");
513     if (htmlend == -1)
514     {
515       text = text + "</body></html>";
516     }
517     if (htmlpos > -1)
518     {
519       if ((doctype > -1 && htmlpos > doctype)
520               || (xmltype > -1 && htmlpos > xmltype))
521       {
522         text = "<html><head></head><body>\n" + text.substring(htmlpos - 1);
523       }
524     }
525     else
526     {
527       text = "<html><head></head><body>\n" + text;
528     }
529     if (text.indexOf("<meta") > -1)
530     {
531       System.err.println("HTML COntent: \n" + text
532               + "<< END HTML CONTENT\n");
533
534     }
535     return text;
536   }
537
538   /**
539    * DOCUMENT ME!
540    * 
541    * @param text
542    *          DOCUMENT ME!
543    */
544   public void appendProgressText(int which, String text)
545   {
546     if (jobPanes == null)
547     {
548       addJobPane();
549     }
550     if (renderAsHtml)
551     {
552       String txt = getHtmlFragment(
553               ((JEditorPane) ((JScrollPane) jobPanes.get(which))
554                       .getViewport().getComponent(0)).getText(), true,
555               false);
556       ((JEditorPane) ((JScrollPane) jobPanes.get(which)).getViewport()
557               .getComponent(0)).setText(ensureHtmlTagged(txt
558               + getHtmlFragment(text, false, true)));
559     }
560     else
561     {
562       ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
563               .getComponent(0)).append(text);
564     }
565   }
566
567   /**
568    * setProgressText(0, text)
569    */
570   public void setProgressText(String text)
571   {
572     setProgressText(0, text);
573   }
574
575   /**
576    * appendProgressText(0, text)
577    */
578   public void appendProgressText(String text)
579   {
580     appendProgressText(0, text);
581   }
582
583   /**
584    * getProgressText(0)
585    */
586   public String getProgressText()
587   {
588     return getProgressText(0);
589   }
590
591   /**
592    * get the tab title for a subjob
593    * 
594    * @param which
595    *          int
596    * @return String
597    */
598   public String getProgressName(int which)
599   {
600     if (jobPanes == null)
601     {
602       addJobPane();
603     }
604     if (subjobs != null)
605     {
606       return subjobs.getTitleAt(which);
607     }
608     else
609     {
610       return ((JScrollPane) jobPanes.get(which)).getViewport()
611               .getComponent(0).getName();
612     }
613   }
614
615   /**
616    * set the tab title for a subjob
617    * 
618    * @param name
619    *          String
620    * @param which
621    *          int
622    */
623   public void setProgressName(String name, int which)
624   {
625     if (subjobs != null)
626     {
627       subjobs.setTitleAt(which, name);
628       subjobs.revalidate();
629       subjobs.repaint();
630     }
631     JScrollPane c = (JScrollPane) jobPanes.get(which);
632     c.getViewport().getComponent(0).setName(name);
633     c.repaint();
634   }
635
636   /**
637    * Gui action for cancelling the current job, if possible.
638    * 
639    * @param e
640    *          DOCUMENT ME!
641    */
642   protected void cancel_actionPerformed(ActionEvent e)
643   {
644     if (!serviceIsCancellable)
645     {
646       // JBPNote : TODO: We should REALLY just tell the WSClientI to cancel
647       // anyhow - it has to stop threads and clean up
648       // JBPNote : TODO: Instead of a warning, we should have an optional 'Are
649       // you sure?' prompt
650       warnUser("This job cannot be cancelled.\nJust close the window.",
651               "Cancel job");
652     }
653     else
654     {
655       thisService.cancelJob();
656     }
657     frame.setClosable(true);
658   }
659
660   /**
661    * Spawns a thread that pops up a warning dialog box with the given message
662    * and title.
663    * 
664    * @param message
665    * @param title
666    */
667   public void warnUser(final String message, final String title)
668   {
669     javax.swing.SwingUtilities.invokeLater(new Runnable()
670     {
671       public void run()
672       {
673         JOptionPane.showInternalMessageDialog(Desktop.desktop, message,
674                 title, JOptionPane.WARNING_MESSAGE);
675
676       }
677     });
678   }
679
680   /**
681    * Set up GUI for user to get at results - and possibly automatically display
682    * them if viewResultsImmediatly is set.
683    */
684   public void setResultsReady()
685   {
686     frame.setClosable(true);
687     buttonPanel.remove(cancel);
688     buttonPanel.add(showResultsNewFrame);
689     if (serviceCanMergeResults)
690     {
691       buttonPanel.add(mergeResults);
692       buttonPanel.setLayout(new GridLayout(2, 1, 5, 5));
693     }
694     buttonPanel.validate();
695     validate();
696     if (viewResultsImmediatly)
697     {
698       showResultsNewFrame.doClick();
699     }
700   }
701
702   /**
703    * called when job has finished but no result objects can be passed back to
704    * user
705    */
706   public void setFinishedNoResults()
707   {
708     frame.setClosable(true);
709     buttonPanel.remove(cancel);
710     buttonPanel.validate();
711     validate();
712   }
713
714   class AnimatedPanel extends JPanel implements Runnable
715   {
716     long startTime = 0;
717
718     BufferedImage offscreen;
719
720     public void run()
721     {
722       startTime = System.currentTimeMillis();
723
724       while (currentStatus < STATE_STOPPED_OK)
725       {
726         try
727         {
728           Thread.sleep(50);
729
730           int units = (int) ((System.currentTimeMillis() - startTime) / 10f);
731           angle += units;
732           angle %= 360;
733           startTime = System.currentTimeMillis();
734
735           if (currentStatus >= STATE_STOPPED_OK)
736           {
737             angle = 0;
738           }
739
740           repaint();
741         } catch (Exception ex)
742         {
743         }
744       }
745
746       cancel.setEnabled(false);
747     }
748
749     void drawPanel()
750     {
751       if (offscreen == null || offscreen.getWidth(this) != getWidth()
752               || offscreen.getHeight(this) != getHeight())
753       {
754         offscreen = new BufferedImage(getWidth(), getHeight(),
755                 BufferedImage.TYPE_INT_ARGB);
756       }
757
758       Graphics2D g = (Graphics2D) offscreen.getGraphics();
759
760       g.setColor(Color.white);
761       g.fillRect(0, 0, getWidth(), getHeight());
762
763       g.setFont(new Font("Arial", Font.BOLD, 12));
764       g.setColor(Color.black);
765
766       switch (currentStatus)
767       {
768       case STATE_QUEUING:
769         g.drawString(title.concat(" - queuing"), 60, 30);
770
771         break;
772
773       case STATE_RUNNING:
774         g.drawString(title.concat(" - running"), 60, 30);
775
776         break;
777
778       case STATE_STOPPED_OK:
779         g.drawString(title.concat(" - complete"), 60, 30);
780
781         break;
782
783       case STATE_CANCELLED_OK:
784         g.drawString(title.concat(" - job cancelled!"), 60, 30);
785
786         break;
787
788       case STATE_STOPPED_ERROR:
789         g.drawString(title.concat(" - job error!"), 60, 30);
790
791         break;
792
793       case STATE_STOPPED_SERVERERROR:
794         g.drawString(title.concat(" - Server Error! (try later)"), 60, 30);
795
796         break;
797       }
798
799       if (image != null)
800       {
801         int x = image.getWidth(this) / 2, y = image.getHeight(this) / 2;
802         g.rotate(Math.toRadians(angle), 10 + x, 10 + y);
803         g.drawImage(image, 10, 10, this);
804         g.rotate(-Math.toRadians(angle), 10 + x, 10 + y);
805       }
806     }
807
808     public void paintComponent(Graphics g1)
809     {
810       drawPanel();
811
812       g1.drawImage(offscreen, 0, 0, this);
813     }
814   }
815
816   boolean renderAsHtml = false;
817
818   public void setRenderAsHtml(boolean b)
819   {
820     renderAsHtml = b;
821   }
822
823   public void hyperlinkUpdate(HyperlinkEvent e)
824   {
825     Desktop.hyperlinkUpdate(e);
826   }
827
828   // methods for implementing IProgressIndicator
829   // need to refactor to a reusable stub class
830   Hashtable progressBars, progressBarHandlers;
831
832   /*
833    * (non-Javadoc)
834    * 
835    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
836    */
837   @Override
838   public void setProgressBar(String message, long id)
839   {
840     if (progressBars == null)
841     {
842       progressBars = new Hashtable();
843       progressBarHandlers = new Hashtable();
844     }
845
846     JPanel progressPanel;
847     Long lId = new Long(id);
848     GridLayout layout = (GridLayout) statusPanel.getLayout();
849     if (progressBars.get(lId) != null)
850     {
851       progressPanel = (JPanel) progressBars.get(new Long(id));
852       statusPanel.remove(progressPanel);
853       progressBars.remove(lId);
854       progressPanel = null;
855       if (message != null)
856       {
857         statusBar.setText(message);
858       }
859       if (progressBarHandlers.contains(lId))
860       {
861         progressBarHandlers.remove(lId);
862       }
863       layout.setRows(layout.getRows() - 1);
864     }
865     else
866     {
867       progressPanel = new JPanel(new BorderLayout(10, 5));
868
869       JProgressBar progressBar = new JProgressBar();
870       progressBar.setIndeterminate(true);
871
872       progressPanel.add(new JLabel(message), BorderLayout.WEST);
873       progressPanel.add(progressBar, BorderLayout.CENTER);
874
875       layout.setRows(layout.getRows() + 1);
876       statusPanel.add(progressPanel);
877
878       progressBars.put(lId, progressPanel);
879     }
880     // update GUI
881     // setMenusForViewport();
882     validate();
883   }
884
885   @Override
886   public void registerHandler(final long id,
887           final IProgressIndicatorHandler handler)
888   {
889     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
890     {
891       throw new Error(
892               "call setProgressBar before registering the progress bar's handler.");
893     }
894     progressBarHandlers.put(new Long(id), handler);
895     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
896     if (handler.canCancel())
897     {
898       JButton cancel = new JButton("Cancel");
899       final IProgressIndicator us = this;
900       cancel.addActionListener(new ActionListener()
901       {
902
903         @Override
904         public void actionPerformed(ActionEvent e)
905         {
906           handler.cancelActivity(id);
907           us.setProgressBar(
908                   "Cancelled "
909                           + ((JLabel) progressPanel.getComponent(0))
910                                   .getText(), id);
911         }
912       });
913       progressPanel.add(cancel, BorderLayout.EAST);
914     }
915   }
916
917   /**
918    * 
919    * @return true if any progress bars are still active
920    */
921   @Override
922   public boolean operationInProgress()
923   {
924     if (progressBars != null && progressBars.size() > 0)
925     {
926       return true;
927     }
928     return false;
929   }
930 }