View Javadoc

1   /*
2    *                Doelan development code
3    *
4    * This code may be freely distributed and modified under the
5    * terms of the GNU General Public Licence.  This should
6    * be distributed with the code. If you do not have a copy,
7    * see:
8    *
9    *      http://www.gnu.org/copyleft/gpl.txt
10   *
11   * Copyright (c) 2004-2005 ENS Microarray Platform
12   * Copyright for this code is held jointly by the individual
13   * authors.  These should be listed in @author doc comments.
14   *
15   * For more information on the Doelan project and its aims,
16   * or to join the Doelan mailing list, visit the home page
17   * at:
18   *
19   *      http://www.transcriptome.ens.fr/doelan
20   */
21  
22  package fr.ens.transcriptome.doelan.gui;
23  
24  import java.awt.BorderLayout;
25  import java.awt.Component;
26  import java.awt.Container;
27  import java.awt.GridLayout;
28  import java.awt.event.ActionEvent;
29  import java.awt.event.ActionListener;
30  import java.awt.event.KeyEvent;
31  import java.awt.event.MouseEvent;
32  import java.awt.event.MouseListener;
33  import java.util.ArrayList;
34  import java.util.HashSet;
35  import java.util.Set;
36  
37  import javax.swing.AbstractAction;
38  import javax.swing.JButton;
39  import javax.swing.JDialog;
40  import javax.swing.JLabel;
41  import javax.swing.JOptionPane;
42  import javax.swing.JPanel;
43  import javax.swing.JPopupMenu;
44  import javax.swing.JScrollPane;
45  import javax.swing.JTable;
46  import javax.swing.SwingConstants;
47  import javax.swing.event.TableModelEvent;
48  import javax.swing.event.TableModelListener;
49  import javax.swing.table.JTableHeader;
50  import javax.swing.table.TableCellRenderer;
51  import javax.swing.table.TableColumn;
52  import javax.swing.table.TableColumnModel;
53  import javax.swing.table.TableModel;
54  
55  import fr.ens.transcriptome.doelan.Core;
56  import fr.ens.transcriptome.doelan.Defaults;
57  import fr.ens.transcriptome.doelan.DoelanException;
58  import fr.ens.transcriptome.doelan.DoelanRegistery;
59  import fr.ens.transcriptome.doelan.algorithms.QualityTest;
60  import fr.ens.transcriptome.doelan.data.QualityTestSuiteURL;
61  import fr.ens.transcriptome.nividic.platform.PlatformException;
62  import fr.ens.transcriptome.nividic.platform.module.AboutModule;
63  import fr.ens.transcriptome.nividic.platform.module.Module;
64  import fr.ens.transcriptome.nividic.platform.module.ModuleLocation;
65  import fr.ens.transcriptome.nividic.platform.module.ModuleManager;
66  import fr.ens.transcriptome.nividic.platform.module.ModuleQuery;
67  import fr.ens.transcriptome.nividic.platform.workflow.Algorithm;
68  import fr.ens.transcriptome.nividic.platform.workflow.WorkflowElement;
69  import fr.ens.transcriptome.nividic.util.SystemUtils;
70  import fr.ens.transcriptome.nividic.util.gui.AboutModuleWidget;
71  import fr.ens.transcriptome.nividic.util.gui.BrowserWidget;
72  import fr.ens.transcriptome.nividic.util.gui.ExternalBrowserLinkListener;
73  
74  /***
75   * @author Laurent Jourdren
76   * @author Christian Kaufhold swing@chka.de
77   */
78  public class TestSuitePanel extends JPanel implements TableModelListener {
79  
80    // For log system
81    // private static Logger log = Logger.getLogger(TestSuitePanel.class);
82  
83    private QualityTestSuiteTableModel model;
84    private JTable table;
85    private QualityTestSuiteURL url;
86    private JButton saveButton = new JButton("Save Test suite");
87    private JButton startButton = new JButton("Start Test suite");
88    private static final String tableTipText = "Right click or double click to modify the test suite.";
89    private JLabel label = new JLabel(tableTipText, SwingConstants.RIGHT);
90  
91    private static final int DOCUMENTATION_DIALOG_WIDTH = 700;
92    private static final int DOCUMENTATION_DIALOG_HEIGHT = 500;
93  
94    private void saveTestSuite() {
95  
96      int response = JOptionPane.showConfirmDialog(table,
97          new String[] {"Save changes ?"}, "Save", JOptionPane.YES_NO_OPTION,
98          JOptionPane.WARNING_MESSAGE);
99  
100     if (response == JOptionPane.YES_OPTION) {
101 
102       try {
103         Core.getCore().saveWorkflow(getUrl());
104       } catch (DoelanException e2) {
105         JOptionPane.showMessageDialog(table, "Error while writing the file : "
106             + e2.getMessage());
107       }
108 
109     }
110   }
111 
112   private ModuleLocation[] getAvailableModules() {
113 
114     ModuleLocation[] modules = model.getAvailableModules();
115 
116     Set instanciedModules = new HashSet();
117 
118     WorkflowElement[] elements = model.getWorkflow().getElements();
119 
120     for (int i = 0; i < elements.length; i++) {
121 
122       Algorithm algo = elements[i].getAlgorithm();
123 
124       if (algo != null && algo instanceof QualityTest && algo instanceof Module)
125         instanciedModules.add(algo.getClass().getName());
126 
127     }
128 
129     ArrayList availableModules = new ArrayList();
130 
131     for (int i = 0; i < modules.length; i++) {
132 
133       Module m = ModuleManager.getManager().loadModule(modules[i]);
134 
135       if (m instanceof QualityTest) {
136         QualityTest qt = (QualityTest) m;
137 
138         if (qt.isAddable()
139             && qt.isShowable()
140             && (qt.isUniqueInstance() ? !instanciedModules.contains(qt
141                 .getClass().getName()) : true))
142           availableModules.add(modules[i]);
143 
144       }
145     }
146 
147     ModuleLocation[] result = new ModuleLocation[availableModules.size()];
148     for (int i = 0; i < availableModules.size(); i++) {
149       result[i] = (ModuleLocation) availableModules.get(i);
150     }
151 
152     return result;
153   }
154 
155   /***
156    * This class is used to handle mouse events on a TableWidgetObject.
157    * @author Laurent Jourdren
158    */
159   class JTableButtonMouseListener implements MouseListener {
160 
161     private JTable table;
162     private EditParametersWidget editParameter;
163     private NewWorkflowElementWidget addAlgo;
164     final QualityTestSuiteTableModel ts = model;
165 
166     private void forwardEventToButton(final MouseEvent e) {
167 
168       if (DoelanRegistery.isAppletMode()
169           || (e.getButton() != MouseEvent.BUTTON3 && !(e.getClickCount() == 2 && e
170               .getButton() == MouseEvent.BUTTON1)))
171 
172         return;
173 
174       final TableColumnModel columnModel = this.table.getColumnModel();
175       final int column = columnModel.getColumnIndexAtX(e.getX());
176       final int row = e.getY() / this.table.getRowHeight();
177 
178       // MouseEvent buttonEvent;
179 
180       if (row >= this.table.getRowCount() || row < 0
181           || column >= this.table.getColumnCount() || column < 0)
182         return;
183 
184       final QualityTest t;
185 
186       t = (QualityTest) model.getAlgorithmAt(row);
187 
188       final int posX = e.getX();
189       final int posY = e.getY();
190 
191       final JPopupMenu menu = new JPopupMenu();
192 
193       // Modify a parameter
194       AbstractAction modifyAction = new AbstractAction("Modify the parameters") {
195         public void actionPerformed(final ActionEvent e) {
196           if (editParameter != null) {
197             editParameter.close();
198           }
199           editParameter = new EditParametersWidget(t);
200           editParameter.setLocation(posX, posY);
201 
202           editParameter.edit();
203         }
204       };
205 
206       menu.add(modifyAction);
207       if (!t.isModifiable())
208         modifyAction.setEnabled(false);
209 
210       menu.addSeparator();
211 
212       // Add a test
213       menu.add(new AbstractAction("Add a test") {
214         public void actionPerformed(final ActionEvent e) {
215           if (addAlgo != null) {
216             addAlgo.close();
217           }
218           addAlgo = new NewWorkflowElementWidget(getAvailableModules());
219           addAlgo.setLocation(posX, posY);
220           addAlgo.setTitle("Add a new test");
221           addAlgo.setModal(true);
222           addAlgo.show();
223 
224           if (addAlgo.isOk()) {
225 
226             if (addAlgo.getIdentifier() == null
227                 || "".equals(addAlgo.getIdentifier())
228                 || ts.getWorkflow().contains(addAlgo.getIdentifier())) {
229               JOptionPane.showMessageDialog(table, "Invalid dentifier");
230             } else {
231               WorkflowElement wfe = ts.getWorkflowElementAt(row);
232               WorkflowElement newWfe = new WorkflowElement(addAlgo
233                   .getIdentifier());
234               newWfe.setModuleQuery(new ModuleQuery(addAlgo
235                   .getModuleLocationSelected()));
236               try {
237 
238                 ts.getWorkflow().insertAfter(wfe, newWfe);
239                 ts.getWorkflow().activate();
240 
241               } catch (PlatformException e1) {
242                 JOptionPane.showMessageDialog(table,
243                     "Error while adding the new element: " + e1.getMessage());
244                 e1.printStackTrace();
245 
246               }
247 
248             }
249 
250           }
251 
252         }
253       });
254 
255       // Remove a parameter
256       AbstractAction removeAction = new AbstractAction("Remove this test") {
257         public void actionPerformed(final ActionEvent e) {
258           final int response = JOptionPane.showConfirmDialog(table,
259               "Remove this test ?");
260 
261           if (response == JOptionPane.YES_OPTION) {
262             WorkflowElement wfe = ts.getWorkflowElementAt(row);
263             if (wfe != null)
264               try {
265                 ts.getWorkflow().removeElementAndJoinPreviousAndNextElements(
266                     wfe);
267               } catch (PlatformException e1) {
268                 JOptionPane.showMessageDialog(table, e1.getMessage());
269               }
270           }
271 
272         }
273       };
274 
275       menu.add(removeAction);
276       if (!t.isDeletable())
277         removeAction.setEnabled(false);
278 
279       /*
280        * menu.addSeparator(); menu.add(new AbstractAction("Save test suite") {
281        * public void actionPerformed(final ActionEvent e) { saveTestSuite(); }
282        * });
283        */
284 
285       menu.addSeparator();
286 
287       AbstractAction documentationAction = new AbstractAction(
288           "Test documentation") {
289 
290         public void actionPerformed(final ActionEvent e) {
291 
292           WorkflowElement wfe = ts.getWorkflowElementAt(row);
293           Algorithm a = wfe.getAlgorithm();
294 
295           if (a instanceof Module) {
296 
297             AboutModule am = ((Module) a).aboutModule();
298 
299             final JDialog dialog = new JDialog();
300             dialog.setTitle(am.getName() + " documentation");
301 
302             String doc;
303             if (am.getHTMLDocumentation() == null) {
304               StringBuffer sb = new StringBuffer();
305               sb.append("<html><body>");
306               sb.append("<h1>Information</h1>");
307               sb.append("<p>No documentation is available for this module</p>");
308 
309               if (am.getWebsite() != null) {
310                 sb
311                     .append("<p>Perhaps more information is available on the <a href=\"");
312                 sb.append(am.getWebsite());
313                 sb.append("\">website</a> of the test.</p>");
314               }
315 
316               sb.append("<br><hr><a href='");
317               sb.append(DoelanRegistery.getAppURL());
318               sb.append("'>");
319               sb.append(Defaults.APP_NAME);
320               sb.append("</a> ");
321               sb.append(DoelanRegistery.getAppVersion());
322               sb.append(", Copyright " + DoelanRegistery.getCopyrightDate()
323                   + " " + "<a href=\"" + DoelanRegistery.getOrganizationURL()
324                   + "\">" + DoelanRegistery.getOrganizationName() + "</a>.\n");
325               sb.append("<!-- PRINT COMMAND HERE -->");
326               sb.append("</body></html>");
327 
328               doc = sb.toString();
329 
330             } else
331               doc = am.getHTMLDocumentation();
332 
333             final Container pane = dialog.getContentPane();
334             pane.setLayout(new BorderLayout());
335 
336             BrowserWidget browser = new BrowserWidget(doc);
337 
338             browser.setHyperLinkListener(new ExternalBrowserLinkListener());
339 
340             JScrollPane jsp = new JScrollPane(browser.getComponent());
341             jsp
342                 .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
343             pane.add(jsp, BorderLayout.CENTER);
344 
345             JButton closeButton = new JButton("Close");
346             closeButton.addActionListener(new ActionListener() {
347 
348               public void actionPerformed(final ActionEvent e) {
349                 dialog.setVisible(false);
350               }
351             });
352 
353             pane.add(closeButton, BorderLayout.SOUTH);
354 
355             dialog.setSize(DOCUMENTATION_DIALOG_WIDTH,
356                 DOCUMENTATION_DIALOG_HEIGHT);
357             dialog.setModal(true);
358             dialog.setVisible(true);
359 
360           } else
361             JOptionPane.showMessageDialog(table, "This test is not a module");
362 
363         }
364 
365       };
366 
367       menu.add(documentationAction);
368 
369       AbstractAction aboutAction = new AbstractAction("About test") {
370 
371         public void actionPerformed(final ActionEvent e) {
372 
373           WorkflowElement wfe = ts.getWorkflowElementAt(row);
374           Algorithm a = wfe.getAlgorithm();
375 
376           if (a instanceof Module) {
377 
378             AboutModule am = ((Module) a).aboutModule();
379 
380             final JDialog dialog = new JDialog();
381             dialog.setTitle("About " + am.getName());
382 
383             Container infoPanel = dialog.getContentPane();
384 
385             infoPanel.setLayout(new BorderLayout());
386             infoPanel.add(new AboutModuleWidget(am), BorderLayout.CENTER);
387 
388             JButton closeButton = new JButton("Close");
389             infoPanel.add(closeButton, BorderLayout.SOUTH);
390             closeButton.addActionListener(new ActionListener() {
391 
392               public void actionPerformed(final ActionEvent e) {
393                 dialog.setVisible(false);
394               }
395             });
396 
397             dialog.pack();
398             dialog.setModal(true);
399             dialog.setResizable(true);
400             dialog.setVisible(true);
401 
402           } else
403             JOptionPane.showMessageDialog(table, "This test is not a module");
404 
405         }
406 
407       };
408 
409       menu.add(aboutAction);
410 
411       menu.show(table.getComponentAt(e.getX(), e.getY()), e.getX(), e.getY());
412 
413     }
414 
415     public JTableButtonMouseListener(final JTable table) {
416       this.table = table;
417     }
418 
419     public void mouseClicked(final MouseEvent e) {
420       forwardEventToButton(e);
421     }
422 
423     public void mouseEntered(final MouseEvent e) {
424       // forwardEventToButton(e);
425     }
426 
427     public void mouseExited(final MouseEvent e) {
428       // forwardEventToButton(e);
429     }
430 
431     public void mousePressed(final MouseEvent e) {
432       // forwardEventToButton(e);
433     }
434 
435     public void mouseReleased(final MouseEvent e) {
436       // forwardEventToButton(e);
437     }
438 
439   }
440 
441   //
442   // Getters
443   //
444 
445   /***
446    * Get the url of the test suite
447    * @return Returns the url
448    */
449   public QualityTestSuiteURL getUrl() {
450     return url;
451   }
452 
453   //
454   // Setters
455   //
456 
457   /***
458    * Set the url of the test suite
459    * @param url The url to set
460    */
461   public void setUrl(final QualityTestSuiteURL url) {
462     this.url = url;
463   }
464 
465   /***
466    * Enable the start button.
467    * @param enable true if the start button must be enabled
468    */
469   public void setStartButtonEnable(final boolean enable) {
470     this.startButton.setEnabled(enable);
471   }
472 
473   //
474   // Other methods
475   //
476 
477   /***
478    * Clear the result column.
479    */
480   public void clearResults() {
481     if (getModel() == null)
482       return;
483     getModel().clearResults();
484 
485     int size = getModel().getRowCount();
486     for (int i = 0; i < size; i++) {
487       getModel().fireTableCellUpdated(i, 3);
488       getModel().fireTableCellUpdated(i, 2);
489     }
490   }
491 
492   //
493   // Constructor
494   //
495 
496   /***
497    * Public constructor.
498    */
499   public TestSuitePanel() {
500     super();
501 
502     this.saveButton.setToolTipText(tableTipText);
503     this.startButton.setToolTipText(tableTipText);
504 
505     if (!SystemUtils.isMacOsX()) {
506       this.saveButton.setMnemonic(KeyEvent.VK_V);
507       this.startButton.setMnemonic(KeyEvent.VK_S);
508     }
509 
510     // add(new JScrollPane(this.table));
511     setLayout(new BorderLayout());
512     this.model = new QualityTestSuiteTableModel();
513 
514     this.model.addTableModelListener(this);
515 
516     this.table = new JTable(model) {
517 
518       // Implement table cell tool tips.
519       public String getToolTipText(MouseEvent e) {
520         java.awt.Point p = e.getPoint();
521         int rowIndex = rowAtPoint(p);
522         int colIndex = columnAtPoint(p);
523         int realColumnIndex = convertColumnIndexToModel(colIndex);
524 
525         if (realColumnIndex == 1) {
526 
527           QualityTestSuiteTableModel model = (QualityTestSuiteTableModel) getModel();
528 
529           return model.getTip(rowIndex);
530         }
531 
532         return null;
533       }
534     };
535     this.table
536         .setToolTipText("Right click or double click to modify the test suite.");
537     this.table.addMouseListener(new JTableButtonMouseListener(this.table));
538     JScrollPane sPane = new JScrollPane(this.table);
539 
540     // sPane.setPreferredSize(new Dimension(750, 500));
541     add(sPane, BorderLayout.CENTER);
542     JPanel southPanel = new JPanel(new BorderLayout());
543     JPanel buttonsPanel = new JPanel();
544 
545     if (!DoelanRegistery.isAppletMode()) {
546 
547       buttonsPanel.add(startButton);
548       startButton.addActionListener(new ActionListener() {
549 
550         public void actionPerformed(ActionEvent e) {
551           Core.getCore().getMainTab().start();
552         }
553 
554       });
555 
556       saveButton.setEnabled(false);
557       saveButton.addActionListener(new ActionListener() {
558 
559         public void actionPerformed(ActionEvent e) {
560           saveTestSuite();
561         }
562 
563       });
564 
565       buttonsPanel.add(saveButton);
566 
567     }
568 
569     southPanel.add(buttonsPanel, BorderLayout.WEST);
570     southPanel.add(label, BorderLayout.EAST);
571 
572     add(southPanel, BorderLayout.SOUTH);
573 
574     if (!DoelanRegistery.isAppletMode())
575       sPane.addMouseListener(new MouseListener() {
576 
577         // Private fields
578         private NewWorkflowElementWidget addAlgo;
579         final QualityTestSuiteTableModel ts = model;
580 
581         private void forwardEventToButton(final MouseEvent e) {
582 
583           if (ts.getWorkflow() == null) {
584             JOptionPane.showMessageDialog(table,
585                 "You must create or load a test suite");
586             return;
587           }
588           if (model.getRowCount() != 0)
589             return;
590 
591           final JPopupMenu menu = new JPopupMenu();
592           final int posX = e.getX();
593           final int posY = e.getY();
594 
595           menu.add(new AbstractAction("Add a test") {
596             public void actionPerformed(final ActionEvent e) {
597 
598               menu.setVisible(false);
599               if (addAlgo != null) {
600                 addAlgo.close();
601               }
602 
603               addAlgo = new NewWorkflowElementWidget(getAvailableModules());
604               addAlgo.setTitle("Add a new test");
605               addAlgo.setModal(true);
606               addAlgo.setLocation(posX, posY);
607               addAlgo.show();
608 
609               if (addAlgo.isOk()) {
610 
611                 if (addAlgo.getIdentifier() == null
612                     || "".equals(addAlgo.getIdentifier())
613                     || ts.getWorkflow().contains(addAlgo.getIdentifier())) {
614                   JOptionPane.showMessageDialog(table, "Invalid dentifier");
615                 } else {
616                   WorkflowElement wfe = ts.getWorkflow().getElement(
617                       Core.LOAD_ALGORITHM);
618                   if (wfe == null) {
619                     JOptionPane.showMessageDialog(table,
620                         "Can't find load data algorithm");
621                     return;
622                   }
623 
624                   WorkflowElement newWfe = new WorkflowElement(addAlgo
625                       .getIdentifier());
626                   newWfe.setModuleQuery(new ModuleQuery(addAlgo
627                       .getModuleLocationSelected()));
628                   try {
629 
630                     ts.getWorkflow().insertAfter(wfe, newWfe);
631                     ts.getWorkflow().activate();
632 
633                   } catch (PlatformException e1) {
634                     JOptionPane.showMessageDialog(table,
635                         "Error while adding the new element: "
636                             + e1.getMessage());
637                   }
638 
639                 }
640               }
641             }
642           });
643           menu.show(table, posX, posY);
644         }
645 
646         public void mouseClicked(final MouseEvent e) {
647           forwardEventToButton(e);
648         }
649 
650         public void mouseEntered(final MouseEvent e) {
651           // forwardEventToButton(e);
652         }
653 
654         public void mouseExited(final MouseEvent e) {
655           // forwardEventToButton(e);
656         }
657 
658         public void mousePressed(final MouseEvent e) {
659           // forwardEventToButton(e);
660         }
661 
662         public void mouseReleased(final MouseEvent e) {
663           // forwardEventToButton(e);
664         }
665 
666       });
667 
668   }
669 
670   /***
671    * Calculate the number of column to display.
672    * @param table Table object
673    */
674   public static void calcColumnWidths(final JTable table) {
675 
676     JTableHeader header = table.getTableHeader();
677 
678     TableCellRenderer defaultHeaderRenderer = null;
679 
680     if (header != null)
681       defaultHeaderRenderer = header.getDefaultRenderer();
682 
683     TableColumnModel columns = table.getColumnModel();
684     TableModel data = table.getModel();
685 
686     int margin = columns.getColumnMargin(); // only
687     // JDK1.3
688 
689     int rowCount = data.getRowCount();
690 
691     int totalWidth = 0;
692 
693     for (int i = columns.getColumnCount() - 1; i >= 0; --i) {
694       TableColumn column = columns.getColumn(i);
695 
696       int columnIndex = column.getModelIndex();
697 
698       int width = -1;
699 
700       TableCellRenderer h = column.getHeaderRenderer();
701 
702       if (h == null)
703         h = defaultHeaderRenderer;
704 
705       // Not explicitly impossible
706       if (h != null) {
707         Component c = h.getTableCellRendererComponent(table, column
708             .getHeaderValue(), false, false, -1, i);
709 
710         width = c.getPreferredSize().width;
711       }
712 
713       for (int row = rowCount - 1; row >= 0; --row) {
714         TableCellRenderer r = table.getCellRenderer(row, i);
715 
716         Component c = r.getTableCellRendererComponent(table, data.getValueAt(
717             row, columnIndex), false, false, row, i);
718 
719         width = Math.max(width, c.getPreferredSize().width);
720       }
721 
722       if (width >= 0)
723         column.setPreferredWidth(width + margin); // <1.3:
724       // without
725       // margin
726       // else
727       // ; // ???
728 
729       totalWidth += column.getPreferredWidth();
730     }
731 
732     // only <1.3: totalWidth += columns.getColumnCount() *
733     // columns.getColumnMargin();
734 
735     /*
736      * If you like; This does not make sense for two many columns! Dimension
737      * size = table.getPreferredScrollableViewportSize(); size.width =
738      * totalWidth; table.setPreferredScrollableViewportSize(size);
739      */
740 
741     // table.sizeColumnsToFit(-1); <1.3; possibly even table.revalidate()
742     // if (header != null)
743     // header.repaint(); only makes sense when the header is visible (only <1.3)
744   }
745 
746   /***
747    * Get the model of the table
748    * @return Returns the model
749    */
750   public QualityTestSuiteTableModel getModel() {
751     return model;
752   }
753 
754   /***
755    * This method is call when the table has been changed.
756    * @param e the event
757    */
758   public void tableChanged(final TableModelEvent e) {
759     calcColumnWidths(this.table);
760     if (getModel().getRowCount() == 0)
761       this.saveButton.setEnabled(false);
762     else
763       this.saveButton.setEnabled(true);
764   }
765 
766 }