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