merge from 2_4_Release branch
[jalview.git] / src / jalview / gui / WebserviceInfo.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import java.util.*;
22
23 import java.awt.*;
24 import java.awt.event.*;
25 import java.awt.image.*;
26 import javax.swing.*;
27
28 import jalview.jbgui.*;
29 import jalview.ws.WSClientI;
30
31 /**
32  * Base class for web service client thread and gui
33  * 
34  * @author $author$
35  * @version $Revision$
36  */
37 public class WebserviceInfo extends GWebserviceInfo
38 {
39
40   /** Job is Queued */
41   public static final int STATE_QUEUING = 0;
42
43   /** Job is Running */
44   public static final int STATE_RUNNING = 1;
45
46   /** Job has finished with no errors */
47   public static final int STATE_STOPPED_OK = 2;
48
49   /** Job has been cancelled with no errors */
50   public static final int STATE_CANCELLED_OK = 3;
51
52   /** job has stopped because of some error */
53   public static final int STATE_STOPPED_ERROR = 4;
54
55   /** job has failed because of some unavoidable service interruption */
56   public static final int STATE_STOPPED_SERVERERROR = 5;
57
58   int currentStatus = STATE_QUEUING;
59
60   Image image;
61
62   int angle = 0;
63
64   String title = "";
65
66   jalview.ws.WSClientI thisService;
67
68   boolean serviceIsCancellable;
69
70   JInternalFrame frame;
71
72   JTabbedPane subjobs = null;
73
74   java.util.Vector jobPanes = null;
75
76   private boolean serviceCanMergeResults = false;
77
78   private boolean viewResultsImmediatly = true;
79
80   // tabbed or not
81   public synchronized int addJobPane()
82   {
83     JScrollPane jobpane = new JScrollPane();
84     JTextArea progressText = new JTextArea();
85     progressText.setFont(new java.awt.Font("Verdana", 0, 10));
86     progressText.setBorder(null);
87     progressText.setEditable(false);
88     progressText.setText("WS Job");
89     progressText.setLineWrap(true);
90     progressText.setWrapStyleWord(true);
91     jobpane.setName("JobPane");
92     jobpane.getViewport().add(progressText, null);
93     jobpane.setBorder(null);
94     if (jobPanes == null)
95     {
96       jobPanes = new Vector();
97     }
98     int newpane = jobPanes.size();
99     jobPanes.add(jobpane);
100
101     if (newpane == 0)
102     {
103       this.add(jobpane, BorderLayout.CENTER);
104     }
105     else
106     {
107       if (newpane == 1)
108       {
109         // revert to a tabbed pane.
110         JScrollPane firstpane;
111         this.remove(firstpane = (JScrollPane) jobPanes.get(0));
112         subjobs = new JTabbedPane();
113         this.add(subjobs, BorderLayout.CENTER);
114         subjobs.add(firstpane);
115         subjobs.setTitleAt(0, firstpane.getName());
116       }
117       subjobs.add(jobpane);
118     }
119     return newpane; // index for accessor methods below
120   }
121
122   /**
123    * Creates a new WebserviceInfo object.
124    * 
125    * @param title
126    *                short name and job type
127    * @param info
128    *                reference or other human readable description
129    */
130   public WebserviceInfo(String title, String info)
131   {
132     init(title, info, 520, 500);
133   }
134
135   /**
136    * Creates a new WebserviceInfo object.
137    * 
138    * @param title
139    *                DOCUMENT ME!
140    * @param info
141    *                DOCUMENT ME!
142    * @param width
143    *                DOCUMENT ME!
144    * @param height
145    *                DOCUMENT ME!
146    */
147   public WebserviceInfo(String title, String info, int width, int height)
148   {
149     init(title, info, width, height);
150   }
151
152   /**
153    * DOCUMENT ME!
154    * 
155    * @return DOCUMENT ME!
156    */
157   public jalview.ws.WSClientI getthisService()
158   {
159     return thisService;
160   }
161
162   /**
163    * Update state of GUI based on client capabilities (like whether the job is
164    * cancellable, whether the 'merge results' button is shown.
165    * 
166    * @param newservice
167    *                service client to query for capabilities
168    */
169   public void setthisService(jalview.ws.WSClientI newservice)
170   {
171     thisService = newservice;
172     serviceIsCancellable = newservice.isCancellable();
173     frame.setClosable(!serviceIsCancellable);
174     serviceCanMergeResults = newservice.canMergeResults();
175     rebuildButtonPanel();
176   }
177
178   private void rebuildButtonPanel()
179   {
180     if (buttonPanel != null)
181     {
182       buttonPanel.removeAll();
183       if (serviceIsCancellable)
184       {
185         buttonPanel.add(cancel);
186         frame.setClosable(false);
187       }
188       else
189       {
190         frame.setClosable(true);
191       }
192     }
193   }
194
195   /**
196    * DOCUMENT ME!
197    * 
198    * @param title
199    *                DOCUMENT ME!
200    * @param info
201    *                DOCUMENT ME!
202    * @param width
203    *                DOCUMENT ME!
204    * @param height
205    *                DOCUMENT ME!
206    */
207   void init(String title, String info, int width, int height)
208   {
209     frame = new JInternalFrame();
210     frame.setContentPane(this);
211     Desktop.addInternalFrame(frame, title, width, height);
212     frame.setClosable(false);
213
214     this.title = title;
215     setInfoText(info);
216
217     java.net.URL url = getClass().getResource("/images/logo.gif");
218     image = java.awt.Toolkit.getDefaultToolkit().createImage(url);
219
220     MediaTracker mt = new MediaTracker(this);
221     mt.addImage(image, 0);
222
223     try
224     {
225       mt.waitForID(0);
226     } catch (Exception ex)
227     {
228     }
229
230     AnimatedPanel ap = new AnimatedPanel();
231     titlePanel.add(ap, BorderLayout.CENTER);
232
233     Thread thread = new Thread(ap);
234     thread.start();
235     final WebserviceInfo thisinfo = this;
236     frame
237             .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
238             {
239               public void internalFrameClosed(
240                       javax.swing.event.InternalFrameEvent evt)
241               {
242                 // System.out.println("Shutting down webservice client");
243                 WSClientI service = thisinfo.getthisService();
244                 if (service != null && service.isCancellable())
245                 {
246                   service.cancelJob();
247                 }
248               };
249             });
250
251   }
252
253   /**
254    * DOCUMENT ME!
255    * 
256    * @param status
257    *                integer status from state constants
258    */
259   public void setStatus(int status)
260   {
261     currentStatus = status;
262   }
263
264   /**
265    * subjob status indicator
266    * 
267    * @param jobpane
268    * @param status
269    */
270   public void setStatus(int jobpane, int status)
271   {
272     if (jobpane < 0 || jobpane >= jobPanes.size())
273     {
274       throw new Error("setStatus called for non-existent job pane."
275               + jobpane);
276     }
277     switch (status)
278     {
279     case STATE_QUEUING:
280       setProgressName(jobpane + " - QUEUED", jobpane);
281       break;
282     case STATE_RUNNING:
283       setProgressName(jobpane + " - RUNNING", jobpane);
284       break;
285     case STATE_STOPPED_OK:
286       setProgressName(jobpane + " - FINISHED", jobpane);
287       break;
288     case STATE_CANCELLED_OK:
289       setProgressName(jobpane + " - CANCELLED", jobpane);
290       break;
291     case STATE_STOPPED_ERROR:
292       setProgressName(jobpane + " - BROKEN", jobpane);
293       break;
294     case STATE_STOPPED_SERVERERROR:
295       setProgressName(jobpane + " - ALERT", jobpane);
296       break;
297     default:
298       setProgressName(jobpane + " - UNKNOWN STATE", jobpane);
299     }
300   }
301
302   /**
303    * DOCUMENT ME!
304    * 
305    * @return DOCUMENT ME!
306    */
307   public String getInfoText()
308   {
309     return infoText.getText();
310   }
311
312   /**
313    * DOCUMENT ME!
314    * 
315    * @param text
316    *                DOCUMENT ME!
317    */
318   public void setInfoText(String text)
319   {
320     infoText.setText(text);
321   }
322
323   /**
324    * DOCUMENT ME!
325    * 
326    * @param text
327    *                DOCUMENT ME!
328    */
329   public void appendInfoText(String text)
330   {
331     infoText.append(text);
332   }
333
334   /**
335    * DOCUMENT ME!
336    * 
337    * @return DOCUMENT ME!
338    */
339   public String getProgressText(int which)
340   {
341     if (jobPanes == null)
342     {
343       addJobPane();
344     }
345     return ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
346             .getComponent(0)).getText();
347   }
348
349   /**
350    * DOCUMENT ME!
351    * 
352    * @param text
353    *                DOCUMENT ME!
354    */
355   public void setProgressText(int which, String text)
356   {
357     if (jobPanes == null)
358     {
359       addJobPane();
360     }
361     ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
362             .getComponent(0)).setText(text);
363   }
364
365   /**
366    * DOCUMENT ME!
367    * 
368    * @param text
369    *                DOCUMENT ME!
370    */
371   public void appendProgressText(int which, String text)
372   {
373     if (jobPanes == null)
374     {
375       addJobPane();
376     }
377     ((JTextArea) ((JScrollPane) jobPanes.get(which)).getViewport()
378             .getComponent(0)).append(text);
379   }
380
381   /**
382    * setProgressText(0, text)
383    */
384   public void setProgressText(String text)
385   {
386     setProgressText(0, text);
387   }
388
389   /**
390    * appendProgressText(0, text)
391    */
392   public void appendProgressText(String text)
393   {
394     appendProgressText(0, text);
395   }
396
397   /**
398    * getProgressText(0)
399    */
400   public String getProgressText()
401   {
402     return getProgressText(0);
403   }
404
405   /**
406    * get the tab title for a subjob
407    * 
408    * @param which
409    *                int
410    * @return String
411    */
412   public String getProgressName(int which)
413   {
414     if (jobPanes == null)
415     {
416       addJobPane();
417     }
418     if (subjobs != null)
419     {
420       return subjobs.getTitleAt(which);
421     }
422     else
423     {
424       return ((JScrollPane) jobPanes.get(which)).getViewport()
425               .getComponent(0).getName();
426     }
427   }
428
429   /**
430    * set the tab title for a subjob
431    * 
432    * @param name
433    *                String
434    * @param which
435    *                int
436    */
437   public void setProgressName(String name, int which)
438   {
439     if (subjobs != null)
440     {
441       subjobs.setTitleAt(which, name);
442       subjobs.revalidate();
443       subjobs.repaint();
444     }
445     JScrollPane c = (JScrollPane) jobPanes.get(which);
446     c.getViewport().getComponent(0).setName(name);
447     c.repaint();
448   }
449
450   /**
451    * Gui action for cancelling the current job, if possible.
452    * 
453    * @param e
454    *                DOCUMENT ME!
455    */
456   protected void cancel_actionPerformed(ActionEvent e)
457   {
458     if (!serviceIsCancellable)
459     {
460       // JBPNote : TODO: We should REALLY just tell the WSClientI to cancel
461       // anyhow - it has to stop threads and clean up
462       // JBPNote : TODO: Instead of a warning, we should have an optional 'Are
463       // you sure?' prompt
464       JOptionPane.showInternalMessageDialog(Desktop.desktop,
465               "This job cannot be cancelled.\nJust close the window.",
466               "Cancel job", JOptionPane.WARNING_MESSAGE);
467     }
468     else
469     {
470       thisService.cancelJob();
471     }
472     frame.setClosable(true);
473   }
474
475   /**
476    * Set up GUI for user to get at results - and possibly automatically display
477    * them if viewResultsImmediatly is set.
478    */
479   public void setResultsReady()
480   {
481     frame.setClosable(true);
482     buttonPanel.remove(cancel);
483     buttonPanel.add(showResultsNewFrame);
484     if (serviceCanMergeResults)
485     {
486       buttonPanel.add(mergeResults);
487       buttonPanel.setLayout(new GridLayout(2, 1, 5, 5));
488     }
489     buttonPanel.validate();
490     validate();
491     if (viewResultsImmediatly)
492     {
493       showResultsNewFrame.doClick();
494     }
495   }
496
497   /**
498    * called when job has finished but no result objects can be passed back to
499    * user
500    */
501   public void setFinishedNoResults()
502   {
503     frame.setClosable(true);
504     buttonPanel.remove(cancel);
505     buttonPanel.validate();
506     validate();
507   }
508
509   class AnimatedPanel extends JPanel implements Runnable
510   {
511     long startTime = 0;
512
513     BufferedImage offscreen;
514
515     public void run()
516     {
517       startTime = System.currentTimeMillis();
518
519       while (currentStatus < STATE_STOPPED_OK)
520       {
521         try
522         {
523           Thread.sleep(50);
524
525           int units = (int) ((System.currentTimeMillis() - startTime) / 10f);
526           angle += units;
527           angle %= 360;
528           startTime = System.currentTimeMillis();
529
530           if (currentStatus >= STATE_STOPPED_OK)
531           {
532             angle = 0;
533           }
534
535           repaint();
536         } catch (Exception ex)
537         {
538         }
539       }
540
541       cancel.setEnabled(false);
542     }
543
544     void drawPanel()
545     {
546       if (offscreen == null || offscreen.getWidth(this) != getWidth()
547               || offscreen.getHeight(this) != getHeight())
548       {
549         offscreen = new BufferedImage(getWidth(), getHeight(),
550                 BufferedImage.TYPE_INT_ARGB);
551       }
552
553       Graphics2D g = (Graphics2D) offscreen.getGraphics();
554
555       g.setColor(Color.white);
556       g.fillRect(0, 0, getWidth(), getHeight());
557
558       g.setFont(new Font("Arial", Font.BOLD, 12));
559       g.setColor(Color.black);
560
561       switch (currentStatus)
562       {
563       case STATE_QUEUING:
564         g.drawString(title.concat(" - queuing"), 60, 30);
565
566         break;
567
568       case STATE_RUNNING:
569         g.drawString(title.concat(" - running"), 60, 30);
570
571         break;
572
573       case STATE_STOPPED_OK:
574         g.drawString(title.concat(" - complete"), 60, 30);
575
576         break;
577
578       case STATE_CANCELLED_OK:
579         g.drawString(title.concat(" - job cancelled!"), 60, 30);
580
581         break;
582
583       case STATE_STOPPED_ERROR:
584         g.drawString(title.concat(" - job error!"), 60, 30);
585
586         break;
587
588       case STATE_STOPPED_SERVERERROR:
589         g.drawString(title.concat(" - Server Error! (try later)"), 60, 30);
590
591         break;
592       }
593
594       if (image != null)
595       {
596         g.rotate(Math.toRadians(angle), 28, 28);
597         g.drawImage(image, 10, 10, this);
598         g.rotate(-Math.toRadians(angle), 28, 28);
599       }
600     }
601
602     public void paintComponent(Graphics g1)
603     {
604       drawPanel();
605
606       g1.drawImage(offscreen, 0, 0, this);
607     }
608   }
609 }