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