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