new callback handler allowing threads that request a progress bar to be cancelled...
[jalview.git] / src / jalview / gui / AlignFrame.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.beans.*;
22 import java.io.*;
23 import java.util.*;
24
25 import java.awt.*;
26 import java.awt.datatransfer.*;
27 import java.awt.dnd.*;
28 import java.awt.event.*;
29 import java.awt.print.*;
30 import javax.swing.*;
31 import javax.swing.event.MenuEvent;
32
33 import jalview.analysis.*;
34 import jalview.commands.*;
35 import jalview.datamodel.*;
36 import jalview.io.*;
37 import jalview.jbgui.*;
38 import jalview.schemes.*;
39 import jalview.ws.*;
40
41 /**
42  * DOCUMENT ME!
43  * 
44  * @author $author$
45  * @version $Revision$
46  */
47 public class AlignFrame extends GAlignFrame implements DropTargetListener,
48         IProgressIndicator
49 {
50
51   /** DOCUMENT ME!! */
52   public static final int DEFAULT_WIDTH = 700;
53
54   /** DOCUMENT ME!! */
55   public static final int DEFAULT_HEIGHT = 500;
56
57   public AlignmentPanel alignPanel;
58
59   AlignViewport viewport;
60
61   Vector alignPanels = new Vector();
62
63   /**
64    * Last format used to load or save alignments in this window
65    */
66   String currentFileFormat = null;
67
68   /**
69    * Current filename for this alignment
70    */
71   String fileName = null;
72
73   /**
74    * Creates a new AlignFrame object with specific width and height.
75    * 
76    * @param al
77    * @param width
78    * @param height
79    */
80   public AlignFrame(AlignmentI al, int width, int height)
81   {
82     this(al, null, width, height);
83   }
84   /**
85    * Creates a new AlignFrame object with specific width, height and sequenceSetId
86    * @param al
87    * @param width
88    * @param height
89    * @param sequenceSetId
90    */
91   public AlignFrame(AlignmentI al, int width, int height, String sequenceSetId)
92   {
93     this(al, null, width, height, sequenceSetId);
94   }
95   /**
96    * Creates a new AlignFrame object with specific width, height and sequenceSetId
97    * @param al
98    * @param width
99    * @param height
100    * @param sequenceSetId
101    * @param viewId
102    */
103   public AlignFrame(AlignmentI al, int width, int height, String sequenceSetId, String viewId)
104   {
105     this(al, null, width, height, sequenceSetId, viewId);
106   }
107   /**
108    * new alignment window with hidden columns
109    * 
110    * @param al
111    *                AlignmentI
112    * @param hiddenColumns
113    *                ColumnSelection or null
114    * @param width Width of alignment frame
115    * @param height height of frame.
116    */
117   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
118           int width, int height)
119   {
120     this(al, hiddenColumns, width, height, null);
121   }
122   /**
123    * Create alignment frame for al with hiddenColumns, a specific width and height, and specific sequenceId
124    * @param al
125    * @param hiddenColumns
126    * @param width
127    * @param height
128    * @param sequenceSetId (may be null)
129    */
130   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns,
131           int width, int height, String sequenceSetId)
132   {
133     this(al, hiddenColumns, width, height, sequenceSetId, null);
134   }
135
136   /**
137    * Create alignment frame for al with hiddenColumns, a specific width and height, and specific sequenceId
138    * @param al
139    * @param hiddenColumns
140    * @param width
141    * @param height
142    * @param sequenceSetId (may be null)
143    * @param viewId (may be null)
144    */
145   public AlignFrame(AlignmentI al, ColumnSelection hiddenColumns, int width, int height,
146           String sequenceSetId, String viewId)
147   {
148     setSize(width, height);
149     viewport = new AlignViewport(al, hiddenColumns, sequenceSetId, viewId);
150
151     alignPanel = new AlignmentPanel(this, viewport);
152
153     if (al.getDataset() == null)
154     {
155       al.setDataset(null);
156     }
157
158     addAlignmentPanel(alignPanel, true);
159     init();
160   }
161
162   /**
163    * Make a new AlignFrame from exisiting alignmentPanels
164    * 
165    * @param ap
166    *                AlignmentPanel
167    * @param av
168    *                AlignViewport
169    */
170   public AlignFrame(AlignmentPanel ap)
171   {
172     viewport = ap.av;
173     alignPanel = ap;
174     addAlignmentPanel(ap, false);
175     init();
176   }
177   /**
178    * initalise the alignframe from the underlying viewport data and the configurations
179    */
180   void init()
181   {
182     if (viewport.conservation == null)
183     {
184       BLOSUM62Colour.setEnabled(false);
185       conservationMenuItem.setEnabled(false);
186       modifyConservation.setEnabled(false);
187       // PIDColour.setEnabled(false);
188       // abovePIDThreshold.setEnabled(false);
189       // modifyPID.setEnabled(false);
190     }
191
192     String sortby = jalview.bin.Cache.getDefault("SORT_ALIGNMENT",
193             "No sort");
194
195     if (sortby.equals("Id"))
196     {
197       sortIDMenuItem_actionPerformed(null);
198     }
199     else if (sortby.equals("Pairwise Identity"))
200     {
201       sortPairwiseMenuItem_actionPerformed(null);
202     }
203
204     if (Desktop.desktop != null)
205     {
206       this.setDropTarget(new java.awt.dnd.DropTarget(this, this));
207       addServiceListeners();
208       setGUINucleotide(viewport.alignment.isNucleotide());
209     }
210
211     setMenusFromViewport(viewport);
212     buildSortByAnnotationScoresMenu();
213     if (viewport.wrapAlignment)
214     {
215       wrapMenuItem_actionPerformed(null);
216     }
217
218     if (jalview.bin.Cache.getDefault("SHOW_OVERVIEW", false))
219     {
220       this.overviewMenuItem_actionPerformed(null);
221     }
222
223     addKeyListener();
224
225   }
226
227   /**
228    * Change the filename and format for the alignment, and enable the 'reload'
229    * button functionality.
230    * 
231    * @param file
232    *                valid filename
233    * @param format
234    *                format of file
235    */
236   public void setFileName(String file, String format)
237   {
238     fileName = file;
239     currentFileFormat = format;
240     reload.setEnabled(true);
241   }
242
243   void addKeyListener()
244   {
245     addKeyListener(new KeyAdapter()
246     {
247       public void keyPressed(KeyEvent evt)
248       {
249         if (viewport.cursorMode
250                 && ((evt.getKeyCode() >= KeyEvent.VK_0 && evt.getKeyCode() <= KeyEvent.VK_9) || (evt
251                         .getKeyCode() >= KeyEvent.VK_NUMPAD0 && evt
252                         .getKeyCode() <= KeyEvent.VK_NUMPAD9))
253                 && Character.isDigit(evt.getKeyChar()))
254           alignPanel.seqPanel.numberPressed(evt.getKeyChar());
255
256         switch (evt.getKeyCode())
257         {
258
259         case 27: // escape key
260           deselectAllSequenceMenuItem_actionPerformed(null);
261
262           break;
263
264         case KeyEvent.VK_DOWN:
265           if (evt.isAltDown() || !viewport.cursorMode)
266             moveSelectedSequences(false);
267           if (viewport.cursorMode)
268             alignPanel.seqPanel.moveCursor(0, 1);
269           break;
270
271         case KeyEvent.VK_UP:
272           if (evt.isAltDown() || !viewport.cursorMode)
273             moveSelectedSequences(true);
274           if (viewport.cursorMode)
275             alignPanel.seqPanel.moveCursor(0, -1);
276
277           break;
278
279         case KeyEvent.VK_LEFT:
280           if (evt.isAltDown() || !viewport.cursorMode)
281             slideSequences(false, alignPanel.seqPanel.getKeyboardNo1());
282           else
283             alignPanel.seqPanel.moveCursor(-1, 0);
284
285           break;
286
287         case KeyEvent.VK_RIGHT:
288           if (evt.isAltDown() || !viewport.cursorMode)
289             slideSequences(true, alignPanel.seqPanel.getKeyboardNo1());
290           else
291             alignPanel.seqPanel.moveCursor(1, 0);
292           break;
293
294         case KeyEvent.VK_SPACE:
295           if (viewport.cursorMode)
296           {
297             alignPanel.seqPanel.insertGapAtCursor(evt.isControlDown()
298                     || evt.isShiftDown() || evt.isAltDown());
299           }
300           break;
301
302         case KeyEvent.VK_DELETE:
303         case KeyEvent.VK_BACK_SPACE:
304           if (!viewport.cursorMode)
305           {
306             cut_actionPerformed(null);
307           }
308           else
309           {
310             alignPanel.seqPanel.deleteGapAtCursor(evt.isControlDown()
311                     || evt.isShiftDown() || evt.isAltDown());
312           }
313
314           break;
315
316         case KeyEvent.VK_S:
317           if (viewport.cursorMode)
318           {
319             alignPanel.seqPanel.setCursorRow();
320           }
321           break;
322         case KeyEvent.VK_C:
323           if (viewport.cursorMode && !evt.isControlDown())
324           {
325             alignPanel.seqPanel.setCursorColumn();
326           }
327           break;
328         case KeyEvent.VK_P:
329           if (viewport.cursorMode)
330           {
331             alignPanel.seqPanel.setCursorPosition();
332           }
333           break;
334
335         case KeyEvent.VK_ENTER:
336         case KeyEvent.VK_COMMA:
337           if (viewport.cursorMode)
338           {
339             alignPanel.seqPanel.setCursorRowAndColumn();
340           }
341           break;
342
343         case KeyEvent.VK_Q:
344           if (viewport.cursorMode)
345           {
346             alignPanel.seqPanel.setSelectionAreaAtCursor(true);
347           }
348           break;
349         case KeyEvent.VK_M:
350           if (viewport.cursorMode)
351           {
352             alignPanel.seqPanel.setSelectionAreaAtCursor(false);
353           }
354           break;
355
356         case KeyEvent.VK_F2:
357           viewport.cursorMode = !viewport.cursorMode;
358           statusBar.setText("Keyboard editing mode is "
359                   + (viewport.cursorMode ? "on" : "off"));
360           if (viewport.cursorMode)
361           {
362             alignPanel.seqPanel.seqCanvas.cursorX = viewport.startRes;
363             alignPanel.seqPanel.seqCanvas.cursorY = viewport.startSeq;
364           }
365           alignPanel.seqPanel.seqCanvas.repaint();
366           break;
367
368         case KeyEvent.VK_F1:
369           try
370           {
371             ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
372             java.net.URL url = javax.help.HelpSet.findHelpSet(cl,
373                     "help/help");
374             javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
375
376             javax.help.HelpBroker hb = hs.createHelpBroker();
377             hb.setCurrentID("home");
378             hb.setDisplayed(true);
379           } catch (Exception ex)
380           {
381             ex.printStackTrace();
382           }
383           break;
384         case KeyEvent.VK_H:
385         {
386           boolean toggleSeqs = !evt.isControlDown();
387           boolean toggleCols = !evt.isShiftDown();
388
389           boolean hide = false;
390
391           SequenceGroup sg = viewport.getSelectionGroup();
392           if (toggleSeqs)
393           {
394             if (sg != null
395                     && sg.getSize() != viewport.alignment.getHeight())
396             {
397               hideSelSequences_actionPerformed(null);
398               hide = true;
399             }
400             else if (!(toggleCols && viewport.colSel.getSelected().size() > 0))
401             {
402               showAllSeqs_actionPerformed(null);
403             }
404           }
405
406           if (toggleCols)
407           {
408             if (viewport.colSel.getSelected().size() > 0)
409             {
410               hideSelColumns_actionPerformed(null);
411               if (!toggleSeqs)
412               {
413                 viewport.selectionGroup = sg;
414               }
415             }
416             else if (!hide)
417             {
418               showAllColumns_actionPerformed(null);
419             }
420           }
421           break;
422         }
423         case KeyEvent.VK_PAGE_UP:
424           if (viewport.wrapAlignment)
425           {
426             alignPanel.scrollUp(true);
427           }
428           else
429           {
430             alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
431                     - viewport.endSeq + viewport.startSeq);
432           }
433           break;
434         case KeyEvent.VK_PAGE_DOWN:
435           if (viewport.wrapAlignment)
436           {
437             alignPanel.scrollUp(false);
438           }
439           else
440           {
441             alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
442                     + viewport.endSeq - viewport.startSeq);
443           }
444           break;
445         }
446       }
447
448       public void keyReleased(KeyEvent evt)
449       {
450         switch (evt.getKeyCode())
451         {
452         case KeyEvent.VK_LEFT:
453           if (evt.isAltDown() || !viewport.cursorMode)
454             viewport.firePropertyChange("alignment", null, viewport
455                     .getAlignment().getSequences());
456           break;
457
458         case KeyEvent.VK_RIGHT:
459           if (evt.isAltDown() || !viewport.cursorMode)
460             viewport.firePropertyChange("alignment", null, viewport
461                     .getAlignment().getSequences());
462           break;
463         }
464       }
465     });
466   }
467
468   public void addAlignmentPanel(final AlignmentPanel ap, boolean newPanel)
469   {
470     ap.alignFrame = this;
471
472     alignPanels.addElement(ap);
473
474     PaintRefresher.Register(ap, ap.av.getSequenceSetId());
475
476     int aSize = alignPanels.size();
477
478     tabbedPane.setVisible(aSize > 1 || ap.av.viewName != null);
479
480     if (aSize == 1 && ap.av.viewName == null)
481     {
482       this.getContentPane().add(ap, BorderLayout.CENTER);
483     }
484     else
485     {
486       if (aSize == 2)
487       {
488         setInitialTabVisible();
489       }
490
491       expandViews.setEnabled(true);
492       gatherViews.setEnabled(true);
493       tabbedPane.addTab(ap.av.viewName, ap);
494
495       ap.setVisible(false);
496     }
497
498     if (newPanel)
499     {
500       if (ap.av.padGaps)
501       {
502         ap.av.alignment.padGaps();
503       }
504       ap.av.updateConservation(ap);
505       ap.av.updateConsensus(ap);
506     }
507   }
508
509   public void setInitialTabVisible()
510   {
511     expandViews.setEnabled(true);
512     gatherViews.setEnabled(true);
513     tabbedPane.setVisible(true);
514     AlignmentPanel first = (AlignmentPanel) alignPanels.firstElement();
515     tabbedPane.addTab(first.av.viewName, first);
516     this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
517   }
518
519   public AlignViewport getViewport()
520   {
521     return viewport;
522   }
523
524   /* Set up intrinsic listeners for dynamically generated GUI bits. */
525   private void addServiceListeners()
526   {
527     final java.beans.PropertyChangeListener thisListener;
528     // Do this once to get current state
529     BuildWebServiceMenu();
530     Desktop.discoverer
531             .addPropertyChangeListener(thisListener = new java.beans.PropertyChangeListener()
532             {
533               public void propertyChange(PropertyChangeEvent evt)
534               {
535                 // System.out.println("Discoverer property change.");
536                 if (evt.getPropertyName().equals("services"))
537                 {
538                   // System.out.println("Rebuilding web service menu");
539                   BuildWebServiceMenu();
540                 }
541               }
542             });
543
544     addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
545     {
546       public void internalFrameClosed(
547               javax.swing.event.InternalFrameEvent evt)
548       {
549         // System.out.println("deregistering discoverer listener");
550         Desktop.discoverer.removePropertyChangeListener(thisListener);
551         closeMenuItem_actionPerformed(true);
552       };
553     });
554   }
555
556   public void setGUINucleotide(boolean nucleotide)
557   {
558     showTranslation.setVisible(nucleotide);
559     conservationMenuItem.setEnabled(!nucleotide);
560     modifyConservation.setEnabled(!nucleotide);
561
562     // Remember AlignFrame always starts as protein
563     if (!nucleotide)
564     {
565       calculateMenu.remove(calculateMenu.getItemCount() - 2);
566     }
567   }
568
569   /**
570    * set up menus for the currently viewport. This may be called after any
571    * operation that affects the data in the current view (selection changed,
572    * etc) to update the menus to reflect the new state.
573    */
574   public void setMenusForViewport()
575   {
576     setMenusFromViewport(viewport);
577   }
578
579   /**
580    * Need to call this method when tabs are selected for multiple views, or when
581    * loading from Jalview2XML.java
582    * 
583    * @param av
584    *                AlignViewport
585    */
586   void setMenusFromViewport(AlignViewport av)
587   {
588     padGapsMenuitem.setSelected(av.padGaps);
589     colourTextMenuItem.setSelected(av.showColourText);
590     abovePIDThreshold.setSelected(av.getAbovePIDThreshold());
591     conservationMenuItem.setSelected(av.getConservationSelected());
592     seqLimits.setSelected(av.getShowJVSuffix());
593     idRightAlign.setSelected(av.rightAlignIds);
594     centreColumnLabelsMenuItem.setState(av.centreColumnLabels);
595     renderGapsMenuItem.setSelected(av.renderGaps);
596     wrapMenuItem.setSelected(av.wrapAlignment);
597     scaleAbove.setVisible(av.wrapAlignment);
598     scaleLeft.setVisible(av.wrapAlignment);
599     scaleRight.setVisible(av.wrapAlignment);
600     annotationPanelMenuItem.setState(av.showAnnotation);
601     viewBoxesMenuItem.setSelected(av.showBoxes);
602     viewTextMenuItem.setSelected(av.showText);
603
604     setColourSelected(ColourSchemeProperty.getColourName(av
605             .getGlobalColourScheme()));
606
607     showSeqFeatures.setSelected(av.showSequenceFeatures);
608     hiddenMarkers.setState(av.showHiddenMarkers);
609     applyToAllGroups.setState(av.colourAppliesToAllGroups);
610     showNpFeatsMenuitem.setSelected(av.isShowNpFeats());
611     showDbRefsMenuitem.setSelected(av.isShowDbRefs());
612
613     setShowProductsEnabled();
614
615     updateEditMenuBar();
616   }
617
618   Hashtable progressBars, progressBarHandlers;
619
620   /*
621    * (non-Javadoc)
622    * 
623    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
624    */
625   public void setProgressBar(String message, long id)
626   {
627     if (progressBars == null)
628     {
629       progressBars = new Hashtable();
630       progressBarHandlers = new Hashtable();
631     }
632
633     JPanel progressPanel;
634     GridLayout layout = (GridLayout) statusPanel.getLayout();
635     if (progressBars.get(new Long(id)) != null)
636     {
637       progressPanel = (JPanel) progressBars.get(new Long(id));
638       statusPanel.remove(progressPanel);
639       progressBars.remove(progressPanel);
640       progressPanel = null;
641       if (message != null)
642       {
643         statusBar.setText(message);
644       }
645       if (progressBarHandlers.contains(new Long(id)))
646       {
647           progressBarHandlers.remove(new Long(id));
648       }
649       layout.setRows(layout.getRows() - 1);
650     }
651     else
652     {
653       progressPanel = new JPanel(new BorderLayout(10, 5));
654
655       JProgressBar progressBar = new JProgressBar();
656       progressBar.setIndeterminate(true);
657
658       progressPanel.add(new JLabel(message), BorderLayout.WEST);
659       progressPanel.add(progressBar, BorderLayout.CENTER);
660
661       layout.setRows(layout.getRows() + 1);
662       statusPanel.add(progressPanel);
663
664       progressBars.put(new Long(id), progressPanel);
665     }
666     // update GUI
667     setMenusForViewport();
668     validate();
669   }
670   public void registerHandler(final long id, final IProgressIndicatorHandler handler)
671   {
672     if (progressBarHandlers==null || !progressBars.contains(new Long(id)))
673     {
674       throw new Error("call setProgressBar before registering the progress bar's handler.");
675     }
676     progressBarHandlers.put(new Long(id), handler);
677     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
678     if (handler.canCancel())
679     {
680       JButton cancel = new JButton("Cancel");
681       final IProgressIndicator us=this;
682       cancel.addActionListener(new ActionListener() {
683
684         public void actionPerformed(ActionEvent e)
685         {
686           handler.cancelActivity(id);
687           us.setProgressBar("Cancelled "+((JLabel)progressPanel.getComponent(0)).getText(), id);
688         }
689       });
690       progressPanel.add(cancel, BorderLayout.EAST);
691     }
692   }
693   /**
694    * 
695    * @return true if any progress bars are still active
696    */
697   public boolean operationInProgress()
698   {
699     if (progressBars != null && progressBars.size() > 0)
700     {
701       return true;
702     }
703     return false;
704   }
705
706   /*
707    * Added so Castor Mapping file can obtain Jalview Version
708    */
709   public String getVersion()
710   {
711     return jalview.bin.Cache.getProperty("VERSION");
712   }
713
714   public FeatureRenderer getFeatureRenderer()
715   {
716     return alignPanel.seqPanel.seqCanvas.getFeatureRenderer();
717   }
718
719   public void fetchSequence_actionPerformed(ActionEvent e)
720   {
721     new SequenceFetcher(this);
722   }
723
724   public void addFromFile_actionPerformed(ActionEvent e)
725   {
726     Desktop.instance.inputLocalFileMenuItem_actionPerformed(viewport);
727   }
728
729   public void reload_actionPerformed(ActionEvent e)
730   {
731     if (fileName != null)
732     {
733       if (currentFileFormat.equals("Jalview"))
734       {
735         JInternalFrame[] frames = Desktop.desktop.getAllFrames();
736         for (int i = 0; i < frames.length; i++)
737         {
738           if (frames[i] instanceof AlignFrame && frames[i] != this
739                   && ((AlignFrame) frames[i]).fileName.equals(fileName))
740           {
741             try
742             {
743               frames[i].setSelected(true);
744               Desktop.instance.closeAssociatedWindows();
745             } catch (java.beans.PropertyVetoException ex)
746             {
747             }
748           }
749
750         }
751         Desktop.instance.closeAssociatedWindows();
752
753         FileLoader loader = new FileLoader();
754         String protocol = fileName.startsWith("http:") ? "URL" : "File";
755         loader.LoadFile(viewport, fileName, protocol, currentFileFormat);
756       }
757       else
758       {
759         Rectangle bounds = this.getBounds();
760
761         FileLoader loader = new FileLoader();
762         String protocol = fileName.startsWith("http:") ? "URL" : "File";
763         AlignFrame newframe = loader.LoadFileWaitTillLoaded(fileName,
764                 protocol, currentFileFormat);
765
766         newframe.setBounds(bounds);
767
768         this.closeMenuItem_actionPerformed(true);
769       }
770     }
771   }
772
773   public void addFromText_actionPerformed(ActionEvent e)
774   {
775     Desktop.instance.inputTextboxMenuItem_actionPerformed(viewport);
776   }
777
778   public void addFromURL_actionPerformed(ActionEvent e)
779   {
780     Desktop.instance.inputURLMenuItem_actionPerformed(viewport);
781   }
782
783   public void save_actionPerformed(ActionEvent e)
784   {
785     if (fileName == null
786             || (currentFileFormat == null 
787                     || !jalview.io.FormatAdapter.isValidIOFormat(currentFileFormat, true))
788             || fileName.startsWith("http"))
789     {
790       saveAs_actionPerformed(null);
791     }
792     else
793     {
794       saveAlignment(fileName, currentFileFormat);
795     }
796   }
797
798   /**
799    * DOCUMENT ME!
800    * 
801    * @param e
802    *                DOCUMENT ME!
803    */
804   public void saveAs_actionPerformed(ActionEvent e)
805   {
806     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
807             .getProperty("LAST_DIRECTORY"),
808             jalview.io.AppletFormatAdapter.WRITABLE_EXTENSIONS,
809             jalview.io.AppletFormatAdapter.WRITABLE_FNAMES,
810             currentFileFormat, false);
811
812     chooser.setFileView(new JalviewFileView());
813     chooser.setDialogTitle("Save Alignment to file");
814     chooser.setToolTipText("Save");
815
816     int value = chooser.showSaveDialog(this);
817
818     if (value == JalviewFileChooser.APPROVE_OPTION)
819     {
820       currentFileFormat = chooser.getSelectedFormat();
821       if (currentFileFormat == null)
822       {
823         JOptionPane.showInternalMessageDialog(Desktop.desktop,
824                 "You must select a file format before saving!",
825                 "File format not specified", JOptionPane.WARNING_MESSAGE);
826         value = chooser.showSaveDialog(this);
827         return;
828       }
829
830       fileName = chooser.getSelectedFile().getPath();
831
832       jalview.bin.Cache.setProperty("DEFAULT_FILE_FORMAT",
833               currentFileFormat);
834
835       jalview.bin.Cache.setProperty("LAST_DIRECTORY", fileName);
836       if (currentFileFormat.indexOf(" ") > -1)
837       {
838         currentFileFormat = currentFileFormat.substring(0,
839                 currentFileFormat.indexOf(" "));
840       }
841       saveAlignment(fileName, currentFileFormat);
842     }
843   }
844
845   public boolean saveAlignment(String file, String format)
846   {
847     boolean success = true;
848
849     if (format.equalsIgnoreCase("Jalview"))
850     {
851       String shortName = title;
852
853       if (shortName.indexOf(java.io.File.separatorChar) > -1)
854       {
855         shortName = shortName.substring(shortName
856                 .lastIndexOf(java.io.File.separatorChar) + 1);
857       }
858
859       success = new Jalview2XML().SaveAlignment(this, file, shortName);
860
861       statusBar.setText("Successfully saved to file: " + fileName + " in "
862               + format + " format.");
863
864     }
865     else
866     {
867       if (!jalview.io.AppletFormatAdapter.isValidFormat(format, true))
868       {
869         // JBPNote need to have a raise_gui flag here
870         JOptionPane.showInternalMessageDialog(this, "Cannot save file "
871                 + fileName + " using format " + format,
872                 "Alignment output format not supported",
873                 JOptionPane.WARNING_MESSAGE);
874         saveAs_actionPerformed(null);
875         return false;
876       }
877
878       String[] omitHidden = null;
879
880       if (viewport.hasHiddenColumns)
881       {
882         int reply = JOptionPane
883                 .showInternalConfirmDialog(
884                         Desktop.desktop,
885                         "The Alignment contains hidden columns."
886                                 + "\nDo you want to save only the visible alignment?",
887                         "Save / Omit Hidden Columns",
888                         JOptionPane.YES_NO_OPTION,
889                         JOptionPane.QUESTION_MESSAGE);
890
891         if (reply == JOptionPane.YES_OPTION)
892         {
893           omitHidden = viewport.getViewAsString(false);
894         }
895       }
896       FormatAdapter f = new FormatAdapter();
897       String output = f.formatSequences(format,
898               (Alignment) viewport.alignment, // class cast exceptions will
899               // occur in the distant future
900               omitHidden, f.getCacheSuffixDefault(format), viewport.colSel);
901
902       if (output == null)
903       {
904         success = false;
905       }
906       else
907       {
908         try
909         {
910           java.io.PrintWriter out = new java.io.PrintWriter(
911                   new java.io.FileWriter(file));
912
913           out.print(output);
914           out.close();
915           this.setTitle(file);
916           statusBar.setText("Successfully saved to file: " + fileName
917                   + " in " + format + " format.");
918         } catch (Exception ex)
919         {
920           success = false;
921           ex.printStackTrace();
922         }
923       }
924     }
925
926     if (!success)
927     {
928       JOptionPane.showInternalMessageDialog(this, "Couldn't save file: "
929               + fileName, "Error Saving File", JOptionPane.WARNING_MESSAGE);
930     }
931
932     return success;
933   }
934
935   /**
936    * DOCUMENT ME!
937    * 
938    * @param e
939    *                DOCUMENT ME!
940    */
941   protected void outputText_actionPerformed(ActionEvent e)
942   {
943     String[] omitHidden = null;
944
945     if (viewport.hasHiddenColumns)
946     {
947       int reply = JOptionPane
948               .showInternalConfirmDialog(
949                       Desktop.desktop,
950                       "The Alignment contains hidden columns."
951                               + "\nDo you want to output only the visible alignment?",
952                       "Save / Omit Hidden Columns",
953                       JOptionPane.YES_NO_OPTION,
954                       JOptionPane.QUESTION_MESSAGE);
955
956       if (reply == JOptionPane.YES_OPTION)
957       {
958         omitHidden = viewport.getViewAsString(false);
959       }
960     }
961
962     CutAndPasteTransfer cap = new CutAndPasteTransfer();
963     cap.setForInput(null);
964     Desktop.addInternalFrame(cap, "Alignment output - "
965             + e.getActionCommand(), 600, 500);
966
967     cap.setText(new FormatAdapter().formatSequences(e.getActionCommand(),
968             viewport.alignment, omitHidden, viewport.colSel));
969   }
970
971   /**
972    * DOCUMENT ME!
973    * 
974    * @param e
975    *                DOCUMENT ME!
976    */
977   protected void htmlMenuItem_actionPerformed(ActionEvent e)
978   {
979     new HTMLOutput(alignPanel, alignPanel.seqPanel.seqCanvas
980             .getSequenceRenderer(), alignPanel.seqPanel.seqCanvas
981             .getFeatureRenderer());
982   }
983
984   public void createImageMap(File file, String image)
985   {
986     alignPanel.makePNGImageMap(file, image);
987   }
988
989   /**
990    * DOCUMENT ME!
991    * 
992    * @param e
993    *                DOCUMENT ME!
994    */
995   public void createPNG(File f)
996   {
997     alignPanel.makePNG(f);
998   }
999
1000   /**
1001    * DOCUMENT ME!
1002    * 
1003    * @param e
1004    *                DOCUMENT ME!
1005    */
1006   public void createEPS(File f)
1007   {
1008     alignPanel.makeEPS(f);
1009   }
1010
1011   public void pageSetup_actionPerformed(ActionEvent e)
1012   {
1013     PrinterJob printJob = PrinterJob.getPrinterJob();
1014     PrintThread.pf = printJob.pageDialog(printJob.defaultPage());
1015   }
1016
1017   /**
1018    * DOCUMENT ME!
1019    * 
1020    * @param e
1021    *                DOCUMENT ME!
1022    */
1023   public void printMenuItem_actionPerformed(ActionEvent e)
1024   {
1025     // Putting in a thread avoids Swing painting problems
1026     PrintThread thread = new PrintThread(alignPanel);
1027     thread.start();
1028   }
1029
1030   public void exportFeatures_actionPerformed(ActionEvent e)
1031   {
1032     new AnnotationExporter().exportFeatures(alignPanel);
1033   }
1034
1035   public void exportAnnotations_actionPerformed(ActionEvent e)
1036   {
1037     new AnnotationExporter().exportAnnotations(alignPanel,
1038             viewport.showAnnotation ? viewport.alignment
1039                     .getAlignmentAnnotation() : null, viewport.alignment
1040                     .getGroups(),
1041             ((Alignment) viewport.alignment).alignmentProperties);
1042   }
1043
1044   public void associatedData_actionPerformed(ActionEvent e)
1045   {
1046     // Pick the tree file
1047     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1048             .getProperty("LAST_DIRECTORY"));
1049     chooser.setFileView(new JalviewFileView());
1050     chooser.setDialogTitle("Load Jalview Annotations or Features File");
1051     chooser.setToolTipText("Load Jalview Annotations / Features file");
1052
1053     int value = chooser.showOpenDialog(null);
1054
1055     if (value == JalviewFileChooser.APPROVE_OPTION)
1056     {
1057       String choice = chooser.getSelectedFile().getPath();
1058       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
1059       loadJalviewDataFile(choice);
1060     }
1061
1062   }
1063
1064   /**
1065    * Close the current view or all views in the alignment frame. 
1066    * If the frame only contains one view then the alignment will be removed from memory.
1067    * 
1068    * @param closeAllTabs 
1069    */
1070   public void closeMenuItem_actionPerformed(boolean closeAllTabs)
1071   {
1072     if (alignPanels != null && alignPanels.size() < 2)
1073     {
1074       closeAllTabs = true;
1075     }
1076
1077     try
1078     {
1079       if (alignPanels != null)
1080       {
1081         if (closeAllTabs)
1082         {
1083           for (int i = 0; i < alignPanels.size(); i++)
1084           {
1085             AlignmentPanel ap = (AlignmentPanel) alignPanels.elementAt(i);
1086             jalview.structure.StructureSelectionManager ssm = 
1087               jalview.structure.StructureSelectionManager
1088                     .getStructureSelectionManager();
1089             ssm.removeStructureViewerListener(ap.seqPanel, null);
1090             ssm.removeSelectionListener(ap.seqPanel);
1091             PaintRefresher.RemoveComponent(ap.seqPanel.seqCanvas);
1092             PaintRefresher.RemoveComponent(ap.idPanel.idCanvas);
1093             PaintRefresher.RemoveComponent(ap);
1094             ap.av.alignment = null;
1095           }
1096         }
1097         else
1098         {
1099           closeView(alignPanel);
1100           viewport = null;
1101         }
1102       }
1103
1104       if (closeAllTabs)
1105       {
1106         this.setClosed(true);
1107       }
1108     } catch (Exception ex)
1109     {
1110       ex.printStackTrace();
1111     }
1112   }
1113
1114   public void closeView(AlignmentPanel alignPanel2)
1115   {
1116     int index = tabbedPane.getSelectedIndex();
1117     int closedindex = tabbedPane.indexOfComponent(alignPanel2);
1118     alignPanels.removeElement(alignPanel2);
1119     PaintRefresher.RemoveComponent(alignPanel2.seqPanel.seqCanvas);
1120     PaintRefresher.RemoveComponent(alignPanel2.idPanel.idCanvas);
1121     PaintRefresher.RemoveComponent(alignPanel2);
1122     alignPanel2.av.alignment = null;
1123     
1124     if (viewport == alignPanel2.av)
1125     {
1126       viewport = null;
1127     }
1128     alignPanel2 = null;
1129     
1130     tabbedPane.removeTabAt(closedindex);
1131     tabbedPane.validate();
1132
1133     if (index > closedindex || index == tabbedPane.getTabCount())
1134     {
1135       // modify currently selected tab index if necessary.
1136       index--;
1137     }
1138
1139     this.tabSelectionChanged(index);    
1140   }
1141
1142   /**
1143    * DOCUMENT ME!
1144    */
1145   void updateEditMenuBar()
1146   {
1147
1148     if (viewport.historyList.size() > 0)
1149     {
1150       undoMenuItem.setEnabled(true);
1151       CommandI command = (CommandI) viewport.historyList.peek();
1152       undoMenuItem.setText("Undo " + command.getDescription());
1153     }
1154     else
1155     {
1156       undoMenuItem.setEnabled(false);
1157       undoMenuItem.setText("Undo");
1158     }
1159
1160     if (viewport.redoList.size() > 0)
1161     {
1162       redoMenuItem.setEnabled(true);
1163
1164       CommandI command = (CommandI) viewport.redoList.peek();
1165       redoMenuItem.setText("Redo " + command.getDescription());
1166     }
1167     else
1168     {
1169       redoMenuItem.setEnabled(false);
1170       redoMenuItem.setText("Redo");
1171     }
1172   }
1173
1174   public void addHistoryItem(CommandI command)
1175   {
1176     if (command.getSize() > 0)
1177     {
1178       viewport.historyList.push(command);
1179       viewport.redoList.clear();
1180       updateEditMenuBar();
1181       viewport.hasHiddenColumns = viewport.colSel.getHiddenColumns() != null;
1182     }
1183   }
1184
1185   /**
1186    * 
1187    * @return alignment objects for all views
1188    */
1189   AlignmentI[] getViewAlignments()
1190   {
1191     if (alignPanels != null)
1192     {
1193       Enumeration e = alignPanels.elements();
1194       AlignmentI[] als = new AlignmentI[alignPanels.size()];
1195       for (int i = 0; e.hasMoreElements(); i++)
1196       {
1197         als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
1198       }
1199       return als;
1200     }
1201     if (viewport != null)
1202     {
1203       return new AlignmentI[]
1204       { viewport.alignment };
1205     }
1206     return null;
1207   }
1208
1209   /**
1210    * DOCUMENT ME!
1211    * 
1212    * @param e
1213    *                DOCUMENT ME!
1214    */
1215   protected void undoMenuItem_actionPerformed(ActionEvent e)
1216   {
1217     if (viewport.historyList.empty())
1218       return;
1219     CommandI command = (CommandI) viewport.historyList.pop();
1220     viewport.redoList.push(command);
1221     command.undoCommand(getViewAlignments());
1222
1223     AlignViewport originalSource = getOriginatingSource(command);
1224     updateEditMenuBar();
1225
1226     if (originalSource != null)
1227     {
1228       originalSource.hasHiddenColumns = viewport.colSel.getHiddenColumns() != null;
1229       originalSource.firePropertyChange("alignment", null,
1230               originalSource.alignment.getSequences());
1231     }
1232   }
1233
1234   /**
1235    * DOCUMENT ME!
1236    * 
1237    * @param e
1238    *                DOCUMENT ME!
1239    */
1240   protected void redoMenuItem_actionPerformed(ActionEvent e)
1241   {
1242     if (viewport.redoList.size() < 1)
1243     {
1244       return;
1245     }
1246
1247     CommandI command = (CommandI) viewport.redoList.pop();
1248     viewport.historyList.push(command);
1249     command.doCommand(getViewAlignments());
1250
1251     AlignViewport originalSource = getOriginatingSource(command);
1252     updateEditMenuBar();
1253
1254     if (originalSource != null)
1255     {
1256       originalSource.hasHiddenColumns = viewport.colSel.getHiddenColumns() != null;
1257       originalSource.firePropertyChange("alignment", null,
1258               originalSource.alignment.getSequences());
1259     }
1260   }
1261
1262   AlignViewport getOriginatingSource(CommandI command)
1263   {
1264     AlignViewport originalSource = null;
1265     // For sequence removal and addition, we need to fire
1266     // the property change event FROM the viewport where the
1267     // original alignment was altered
1268     AlignmentI al = null;
1269     if (command instanceof EditCommand)
1270     {
1271       EditCommand editCommand = (EditCommand) command;
1272       al = editCommand.getAlignment();
1273       Vector comps = (Vector) PaintRefresher.components.get(viewport
1274               .getSequenceSetId());
1275
1276       for (int i = 0; i < comps.size(); i++)
1277       {
1278         if (comps.elementAt(i) instanceof AlignmentPanel)
1279         {
1280           if (al == ((AlignmentPanel) comps.elementAt(i)).av.alignment)
1281           {
1282             originalSource = ((AlignmentPanel) comps.elementAt(i)).av;
1283             break;
1284           }
1285         }
1286       }
1287     }
1288
1289     if (originalSource == null)
1290     {
1291       // The original view is closed, we must validate
1292       // the current view against the closed view first
1293       if (al != null)
1294       {
1295         PaintRefresher.validateSequences(al, viewport.alignment);
1296       }
1297
1298       originalSource = viewport;
1299     }
1300
1301     return originalSource;
1302   }
1303
1304   /**
1305    * DOCUMENT ME!
1306    * 
1307    * @param up
1308    *                DOCUMENT ME!
1309    */
1310   public void moveSelectedSequences(boolean up)
1311   {
1312     SequenceGroup sg = viewport.getSelectionGroup();
1313
1314     if (sg == null)
1315     {
1316       return;
1317     }
1318
1319     if (up)
1320     {
1321       for (int i = 1; i < viewport.alignment.getHeight(); i++)
1322       {
1323         SequenceI seq = viewport.alignment.getSequenceAt(i);
1324
1325         if (!sg.getSequences(null).contains(seq))
1326         {
1327           continue;
1328         }
1329
1330         SequenceI temp = viewport.alignment.getSequenceAt(i - 1);
1331
1332         if (sg.getSequences(null).contains(temp))
1333         {
1334           continue;
1335         }
1336
1337         viewport.alignment.getSequences().setElementAt(temp, i);
1338         viewport.alignment.getSequences().setElementAt(seq, i - 1);
1339       }
1340     }
1341     else
1342     {
1343       for (int i = viewport.alignment.getHeight() - 2; i > -1; i--)
1344       {
1345         SequenceI seq = viewport.alignment.getSequenceAt(i);
1346
1347         if (!sg.getSequences(null).contains(seq))
1348         {
1349           continue;
1350         }
1351
1352         SequenceI temp = viewport.alignment.getSequenceAt(i + 1);
1353
1354         if (sg.getSequences(null).contains(temp))
1355         {
1356           continue;
1357         }
1358
1359         viewport.alignment.getSequences().setElementAt(temp, i);
1360         viewport.alignment.getSequences().setElementAt(seq, i + 1);
1361       }
1362     }
1363
1364     alignPanel.paintAlignment(true);
1365   }
1366
1367   synchronized void slideSequences(boolean right, int size)
1368   {
1369     Vector sg = new Vector();
1370     if (viewport.cursorMode)
1371     {
1372       sg.addElement(viewport.alignment
1373               .getSequenceAt(alignPanel.seqPanel.seqCanvas.cursorY));
1374     }
1375     else if (viewport.getSelectionGroup() != null
1376             && viewport.getSelectionGroup().getSize() != viewport.alignment
1377                     .getHeight())
1378     {
1379       sg = viewport.getSelectionGroup().getSequences(
1380               viewport.hiddenRepSequences);
1381     }
1382
1383     if (sg.size() < 1)
1384     {
1385       return;
1386     }
1387
1388     Vector invertGroup = new Vector();
1389
1390     for (int i = 0; i < viewport.alignment.getHeight(); i++)
1391     {
1392       if (!sg.contains(viewport.alignment.getSequenceAt(i)))
1393         invertGroup.add(viewport.alignment.getSequenceAt(i));
1394     }
1395
1396     SequenceI[] seqs1 = new SequenceI[sg.size()];
1397     for (int i = 0; i < sg.size(); i++)
1398       seqs1[i] = (SequenceI) sg.elementAt(i);
1399
1400     SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
1401     for (int i = 0; i < invertGroup.size(); i++)
1402       seqs2[i] = (SequenceI) invertGroup.elementAt(i);
1403
1404     SlideSequencesCommand ssc;
1405     if (right)
1406       ssc = new SlideSequencesCommand("Slide Sequences", seqs2, seqs1,
1407               size, viewport.getGapCharacter());
1408     else
1409       ssc = new SlideSequencesCommand("Slide Sequences", seqs1, seqs2,
1410               size, viewport.getGapCharacter());
1411
1412     int groupAdjustment = 0;
1413     if (ssc.getGapsInsertedBegin() && right)
1414     {
1415       if (viewport.cursorMode)
1416         alignPanel.seqPanel.moveCursor(size, 0);
1417       else
1418         groupAdjustment = size;
1419     }
1420     else if (!ssc.getGapsInsertedBegin() && !right)
1421     {
1422       if (viewport.cursorMode)
1423         alignPanel.seqPanel.moveCursor(-size, 0);
1424       else
1425         groupAdjustment = -size;
1426     }
1427
1428     if (groupAdjustment != 0)
1429     {
1430       viewport.getSelectionGroup().setStartRes(
1431               viewport.getSelectionGroup().getStartRes() + groupAdjustment);
1432       viewport.getSelectionGroup().setEndRes(
1433               viewport.getSelectionGroup().getEndRes() + groupAdjustment);
1434     }
1435
1436     boolean appendHistoryItem = false;
1437     if (viewport.historyList != null && viewport.historyList.size() > 0
1438             && viewport.historyList.peek() instanceof SlideSequencesCommand)
1439     {
1440       appendHistoryItem = ssc
1441               .appendSlideCommand((SlideSequencesCommand) viewport.historyList
1442                       .peek());
1443     }
1444
1445     if (!appendHistoryItem)
1446       addHistoryItem(ssc);
1447
1448     repaint();
1449   }
1450
1451   /**
1452    * DOCUMENT ME!
1453    * 
1454    * @param e
1455    *                DOCUMENT ME!
1456    */
1457   protected void copy_actionPerformed(ActionEvent e)
1458   {
1459     System.gc();
1460     if (viewport.getSelectionGroup() == null)
1461     {
1462       return;
1463     }
1464     // TODO: preserve the ordering of displayed alignment annotation in any
1465     // internal paste (particularly sequence associated annotation)
1466     SequenceI[] seqs = viewport.getSelectionAsNewSequence();
1467     String[] omitHidden = null;
1468
1469     if (viewport.hasHiddenColumns)
1470     {
1471       omitHidden = viewport.getViewAsString(true);
1472     }
1473
1474     String output = new FormatAdapter().formatSequences("Fasta", seqs,
1475             omitHidden);
1476
1477     StringSelection ss = new StringSelection(output);
1478
1479     try
1480     {
1481       jalview.gui.Desktop.internalCopy = true;
1482       // Its really worth setting the clipboard contents
1483       // to empty before setting the large StringSelection!!
1484       Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
1485               new StringSelection(""), null);
1486
1487       Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss,
1488               Desktop.instance);
1489     } catch (OutOfMemoryError er)
1490     {
1491       new OOMWarning("copying region", er);
1492       return;
1493     }
1494
1495     Vector hiddenColumns = null;
1496     if (viewport.hasHiddenColumns)
1497     {
1498       hiddenColumns = new Vector();
1499       int hiddenOffset = viewport.getSelectionGroup().getStartRes();
1500       for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns()
1501               .size(); i++)
1502       {
1503         int[] region = (int[]) viewport.getColumnSelection()
1504                 .getHiddenColumns().elementAt(i);
1505
1506         hiddenColumns.addElement(new int[]
1507         { region[0] - hiddenOffset, region[1] - hiddenOffset });
1508       }
1509     }
1510
1511     Desktop.jalviewClipboard = new Object[]
1512     { seqs, viewport.alignment.getDataset(), hiddenColumns };
1513     statusBar.setText("Copied " + seqs.length + " sequences to clipboard.");
1514   }
1515
1516   /**
1517    * DOCUMENT ME!
1518    * 
1519    * @param e
1520    *                DOCUMENT ME!
1521    */
1522   protected void pasteNew_actionPerformed(ActionEvent e)
1523   {
1524     paste(true);
1525   }
1526
1527   /**
1528    * DOCUMENT ME!
1529    * 
1530    * @param e
1531    *                DOCUMENT ME!
1532    */
1533   protected void pasteThis_actionPerformed(ActionEvent e)
1534   {
1535     paste(false);
1536   }
1537
1538   /**
1539    * Paste contents of Jalview clipboard
1540    * 
1541    * @param newAlignment
1542    *                true to paste to a new alignment, otherwise add to this.
1543    */
1544   void paste(boolean newAlignment)
1545   {
1546     boolean externalPaste = true;
1547     try
1548     {
1549       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
1550       Transferable contents = c.getContents(this);
1551
1552       if (contents == null)
1553       {
1554         return;
1555       }
1556
1557       String str, format;
1558       try
1559       {
1560         str = (String) contents.getTransferData(DataFlavor.stringFlavor);
1561         if (str.length() < 1)
1562         {
1563           return;
1564         }
1565
1566         format = new IdentifyFile().Identify(str, "Paste");
1567
1568       } catch (OutOfMemoryError er)
1569       {
1570         new OOMWarning("Out of memory pasting sequences!!", er);
1571         return;
1572       }
1573
1574       SequenceI[] sequences;
1575       boolean annotationAdded = false;
1576       AlignmentI alignment = null;
1577
1578       if (Desktop.jalviewClipboard != null)
1579       {
1580         // The clipboard was filled from within Jalview, we must use the
1581         // sequences
1582         // And dataset from the copied alignment
1583         SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
1584         // be doubly sure that we create *new* sequence objects.
1585         sequences = new SequenceI[newseq.length];
1586         for (int i = 0; i < newseq.length; i++)
1587         {
1588           sequences[i] = new Sequence(newseq[i]);
1589         }
1590         alignment = new Alignment(sequences);
1591         externalPaste = false;
1592       }
1593       else
1594       {
1595         // parse the clipboard as an alignment.
1596         alignment = new FormatAdapter().readFile(str, "Paste", format);
1597         sequences = alignment.getSequencesArray();
1598       }
1599
1600       int alwidth = 0;
1601
1602       if (newAlignment)
1603       {
1604
1605         if (Desktop.jalviewClipboard != null)
1606         {
1607           // dataset is inherited
1608           alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
1609         }
1610         else
1611         {
1612           // new dataset is constructed
1613           alignment.setDataset(null);
1614         }
1615         alwidth = alignment.getWidth() + 1;
1616       }
1617       else
1618       {
1619         AlignmentI pastedal = alignment; // preserve pasted alignment object
1620         // Add pasted sequences and dataset into existing alignment.
1621         alignment = viewport.getAlignment();
1622         alwidth = alignment.getWidth() + 1;
1623         // decide if we need to import sequences from an existing dataset
1624         boolean importDs = Desktop.jalviewClipboard != null
1625                 && Desktop.jalviewClipboard[1] != alignment.getDataset();
1626         // importDs==true instructs us to copy over new dataset sequences from
1627         // an existing alignment
1628         Vector newDs = (importDs) ? new Vector() : null; // used to create
1629         // minimum dataset set
1630
1631         for (int i = 0; i < sequences.length; i++)
1632         {
1633           if (importDs)
1634           {
1635             newDs.addElement(null);
1636           }
1637           SequenceI ds = sequences[i].getDatasetSequence(); // null for a simple
1638           // paste
1639           if (importDs && ds != null)
1640           {
1641             if (!newDs.contains(ds))
1642             {
1643               newDs.setElementAt(ds, i);
1644               ds = new Sequence(ds);
1645               // update with new dataset sequence
1646               sequences[i].setDatasetSequence(ds);
1647             }
1648             else
1649             {
1650               ds = sequences[newDs.indexOf(ds)].getDatasetSequence();
1651             }
1652           }
1653           else
1654           {
1655             // copy and derive new dataset sequence
1656             sequences[i] = sequences[i].deriveSequence();
1657             alignment.getDataset().addSequence(
1658                     sequences[i].getDatasetSequence());
1659             // TODO: avoid creation of duplicate dataset sequences with a
1660             // 'contains' method using SequenceI.equals()/SequenceI.contains()
1661           }
1662           alignment.addSequence(sequences[i]); // merges dataset
1663         }
1664         if (newDs != null)
1665         {
1666           newDs.clear(); // tidy up
1667         }
1668         if (pastedal.getAlignmentAnnotation() != null)
1669         {
1670           // Add any annotation attached to alignment.
1671           AlignmentAnnotation[] alann = pastedal.getAlignmentAnnotation();
1672           for (int i = 0; i < alann.length; i++)
1673           {
1674             annotationAdded = true;
1675             if (alann[i].sequenceRef == null && !alann[i].autoCalculated)
1676             {
1677               AlignmentAnnotation newann = new AlignmentAnnotation(alann[i]);
1678               newann.padAnnotation(alwidth);
1679               alignment.addAnnotation(newann);
1680             }
1681           }
1682         }
1683       }
1684       if (!newAlignment)
1685       {
1686         // /////
1687         // ADD HISTORY ITEM
1688         //
1689         addHistoryItem(new EditCommand("Add sequences", EditCommand.PASTE,
1690                 sequences, 0, alignment.getWidth(), alignment));
1691       }
1692       // Add any annotations attached to sequences
1693       for (int i = 0; i < sequences.length; i++)
1694       {
1695         if (sequences[i].getAnnotation() != null)
1696         {
1697           for (int a = 0; a < sequences[i].getAnnotation().length; a++)
1698           {
1699             annotationAdded = true;
1700             sequences[i].getAnnotation()[a].adjustForAlignment();
1701             sequences[i].getAnnotation()[a].padAnnotation(alwidth);
1702             alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation
1703             // was
1704             // duplicated
1705             // earlier
1706             alignment
1707                     .setAnnotationIndex(sequences[i].getAnnotation()[a], a);
1708           }
1709         }
1710       }
1711       if (!newAlignment)
1712       {
1713
1714         // propagate alignment changed.
1715         viewport.setEndSeq(alignment.getHeight());
1716         if (annotationAdded)
1717         {
1718           // Duplicate sequence annotation in all views.
1719           AlignmentI[] alview = this.getViewAlignments();
1720           for (int i = 0; i < sequences.length; i++)
1721           {
1722             AlignmentAnnotation sann[] = sequences[i].getAnnotation();
1723             if (sann == null)
1724               continue;
1725             for (int avnum = 0; avnum < alview.length; avnum++)
1726             {
1727               if (alview[avnum] != alignment)
1728               {
1729                 // duplicate in a view other than the one with input focus
1730                 int avwidth = alview[avnum].getWidth() + 1;
1731                 // this relies on sann being preserved after we
1732                 // modify the sequence's annotation array for each duplication
1733                 for (int a = 0; a < sann.length; a++)
1734                 {
1735                   AlignmentAnnotation newann = new AlignmentAnnotation(
1736                           sann[a]);
1737                   sequences[i].addAlignmentAnnotation(newann);
1738                   newann.padAnnotation(avwidth);
1739                   alview[avnum].addAnnotation(newann); // annotation was
1740                   // duplicated earlier
1741                   alview[avnum].setAnnotationIndex(newann, a);
1742                 }
1743               }
1744             }
1745           }
1746           buildSortByAnnotationScoresMenu();
1747         }
1748         viewport.firePropertyChange("alignment", null, alignment
1749                 .getSequences());
1750
1751       }
1752       else
1753       {
1754         AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH,
1755                 DEFAULT_HEIGHT);
1756         String newtitle = new String("Copied sequences");
1757
1758         if (Desktop.jalviewClipboard != null
1759                 && Desktop.jalviewClipboard[2] != null)
1760         {
1761           Vector hc = (Vector) Desktop.jalviewClipboard[2];
1762           for (int i = 0; i < hc.size(); i++)
1763           {
1764             int[] region = (int[]) hc.elementAt(i);
1765             af.viewport.hideColumns(region[0], region[1]);
1766           }
1767         }
1768
1769         // >>>This is a fix for the moment, until a better solution is
1770         // found!!<<<
1771         af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
1772                 .transferSettings(
1773                         alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
1774
1775         // TODO: maintain provenance of an alignment, rather than just make the
1776         // title a concatenation of operations.
1777         if (!externalPaste)
1778         {
1779           if (title.startsWith("Copied sequences"))
1780           {
1781             newtitle = title;
1782           }
1783           else
1784           {
1785             newtitle = newtitle.concat("- from " + title);
1786           }
1787         }
1788         else
1789         {
1790           newtitle = new String("Pasted sequences");
1791         }
1792
1793         Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
1794                 DEFAULT_HEIGHT);
1795
1796       }
1797
1798     } catch (Exception ex)
1799     {
1800       ex.printStackTrace();
1801       System.out.println("Exception whilst pasting: " + ex);
1802       // could be anything being pasted in here
1803     }
1804
1805   }
1806
1807   /**
1808    * DOCUMENT ME!
1809    * 
1810    * @param e
1811    *                DOCUMENT ME!
1812    */
1813   protected void cut_actionPerformed(ActionEvent e)
1814   {
1815     copy_actionPerformed(null);
1816     delete_actionPerformed(null);
1817   }
1818
1819   /**
1820    * DOCUMENT ME!
1821    * 
1822    * @param e
1823    *                DOCUMENT ME!
1824    */
1825   protected void delete_actionPerformed(ActionEvent evt)
1826   {
1827
1828     SequenceGroup sg = viewport.getSelectionGroup();
1829     if (sg == null)
1830     {
1831       return;
1832     }
1833
1834     Vector seqs = new Vector();
1835     SequenceI seq;
1836     for (int i = 0; i < sg.getSize(); i++)
1837     {
1838       seq = sg.getSequenceAt(i);
1839       seqs.addElement(seq);
1840     }
1841
1842     // If the cut affects all sequences, remove highlighted columns
1843     if (sg.getSize() == viewport.alignment.getHeight())
1844     {
1845       viewport.getColumnSelection().removeElements(sg.getStartRes(),
1846               sg.getEndRes() + 1);
1847     }
1848
1849     SequenceI[] cut = new SequenceI[seqs.size()];
1850     for (int i = 0; i < seqs.size(); i++)
1851     {
1852       cut[i] = (SequenceI) seqs.elementAt(i);
1853     }
1854
1855     /*
1856      * //ADD HISTORY ITEM
1857      */
1858     addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT, cut,
1859             sg.getStartRes(), sg.getEndRes() - sg.getStartRes() + 1,
1860             viewport.alignment));
1861
1862     viewport.setSelectionGroup(null);
1863     viewport.sendSelection();
1864     viewport.alignment.deleteGroup(sg);
1865
1866     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
1867             .getSequences());
1868     if (viewport.getAlignment().getHeight() < 1)
1869     {
1870       try
1871       {
1872         this.setClosed(true);
1873       } catch (Exception ex)
1874       {
1875       }
1876     }
1877   }
1878
1879   /**
1880    * DOCUMENT ME!
1881    * 
1882    * @param e
1883    *                DOCUMENT ME!
1884    */
1885   protected void deleteGroups_actionPerformed(ActionEvent e)
1886   {
1887     viewport.alignment.deleteAllGroups();
1888     viewport.sequenceColours = null;
1889     viewport.setSelectionGroup(null);
1890     PaintRefresher.Refresh(this, viewport.getSequenceSetId());
1891     alignPanel.paintAlignment(true);
1892   }
1893
1894   /**
1895    * DOCUMENT ME!
1896    * 
1897    * @param e
1898    *                DOCUMENT ME!
1899    */
1900   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
1901   {
1902     SequenceGroup sg = new SequenceGroup();
1903
1904     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
1905     {
1906       sg.addSequence(viewport.getAlignment().getSequenceAt(i), false);
1907     }
1908
1909     sg.setEndRes(viewport.alignment.getWidth() - 1);
1910     viewport.setSelectionGroup(sg);
1911     viewport.sendSelection();
1912     alignPanel.paintAlignment(true);
1913     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
1914   }
1915
1916   /**
1917    * DOCUMENT ME!
1918    * 
1919    * @param e
1920    *                DOCUMENT ME!
1921    */
1922   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
1923   {
1924     if (viewport.cursorMode)
1925     {
1926       alignPanel.seqPanel.keyboardNo1 = null;
1927       alignPanel.seqPanel.keyboardNo2 = null;
1928     }
1929     viewport.setSelectionGroup(null);
1930     viewport.getColumnSelection().clear();
1931     viewport.setSelectionGroup(null);
1932     alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
1933     alignPanel.idPanel.idCanvas.searchResults = null;
1934     alignPanel.paintAlignment(true);
1935     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
1936   }
1937
1938   /**
1939    * DOCUMENT ME!
1940    * 
1941    * @param e
1942    *                DOCUMENT ME!
1943    */
1944   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
1945   {
1946     SequenceGroup sg = viewport.getSelectionGroup();
1947
1948     if (sg == null)
1949     {
1950       selectAllSequenceMenuItem_actionPerformed(null);
1951
1952       return;
1953     }
1954
1955     for (int i = 0; i < viewport.getAlignment().getSequences().size(); i++)
1956     {
1957       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
1958     }
1959
1960     alignPanel.paintAlignment(true);
1961     viewport.sendSelection();
1962     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
1963   }
1964
1965   public void invertColSel_actionPerformed(ActionEvent e)
1966   {
1967     viewport.invertColumnSelection();
1968     alignPanel.paintAlignment(true);
1969   }
1970
1971   /**
1972    * DOCUMENT ME!
1973    * 
1974    * @param e
1975    *                DOCUMENT ME!
1976    */
1977   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
1978   {
1979     trimAlignment(true);
1980   }
1981
1982   /**
1983    * DOCUMENT ME!
1984    * 
1985    * @param e
1986    *                DOCUMENT ME!
1987    */
1988   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
1989   {
1990     trimAlignment(false);
1991   }
1992
1993   void trimAlignment(boolean trimLeft)
1994   {
1995     ColumnSelection colSel = viewport.getColumnSelection();
1996     int column;
1997
1998     if (colSel.size() > 0)
1999     {
2000       if (trimLeft)
2001       {
2002         column = colSel.getMin();
2003       }
2004       else
2005       {
2006         column = colSel.getMax();
2007       }
2008
2009       SequenceI[] seqs;
2010       if (viewport.getSelectionGroup() != null)
2011       {
2012         seqs = viewport.getSelectionGroup().getSequencesAsArray(
2013                 viewport.hiddenRepSequences);
2014       }
2015       else
2016       {
2017         seqs = viewport.alignment.getSequencesArray();
2018       }
2019
2020       TrimRegionCommand trimRegion;
2021       if (trimLeft)
2022       {
2023         trimRegion = new TrimRegionCommand("Remove Left",
2024                 TrimRegionCommand.TRIM_LEFT, seqs, column,
2025                 viewport.alignment, viewport.colSel,
2026                 viewport.selectionGroup);
2027         viewport.setStartRes(0);
2028       }
2029       else
2030       {
2031         trimRegion = new TrimRegionCommand("Remove Right",
2032                 TrimRegionCommand.TRIM_RIGHT, seqs, column,
2033                 viewport.alignment, viewport.colSel,
2034                 viewport.selectionGroup);
2035       }
2036
2037       statusBar.setText("Removed " + trimRegion.getSize() + " columns.");
2038
2039       addHistoryItem(trimRegion);
2040
2041       Vector groups = viewport.alignment.getGroups();
2042
2043       for (int i = 0; i < groups.size(); i++)
2044       {
2045         SequenceGroup sg = (SequenceGroup) groups.get(i);
2046
2047         if ((trimLeft && !sg.adjustForRemoveLeft(column))
2048                 || (!trimLeft && !sg.adjustForRemoveRight(column)))
2049         {
2050           viewport.alignment.deleteGroup(sg);
2051         }
2052       }
2053
2054       viewport.firePropertyChange("alignment", null, viewport
2055               .getAlignment().getSequences());
2056     }
2057   }
2058
2059   /**
2060    * DOCUMENT ME!
2061    * 
2062    * @param e
2063    *                DOCUMENT ME!
2064    */
2065   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
2066   {
2067     int start = 0, end = viewport.alignment.getWidth() - 1;
2068
2069     SequenceI[] seqs;
2070     if (viewport.getSelectionGroup() != null)
2071     {
2072       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2073               viewport.hiddenRepSequences);
2074       start = viewport.getSelectionGroup().getStartRes();
2075       end = viewport.getSelectionGroup().getEndRes();
2076     }
2077     else
2078     {
2079       seqs = viewport.alignment.getSequencesArray();
2080     }
2081
2082     RemoveGapColCommand removeGapCols = new RemoveGapColCommand(
2083             "Remove Gapped Columns", seqs, start, end, viewport.alignment);
2084
2085     addHistoryItem(removeGapCols);
2086
2087     statusBar.setText("Removed " + removeGapCols.getSize()
2088             + " empty columns.");
2089
2090     // This is to maintain viewport position on first residue
2091     // of first sequence
2092     SequenceI seq = viewport.alignment.getSequenceAt(0);
2093     int startRes = seq.findPosition(viewport.startRes);
2094     // ShiftList shifts;
2095     // viewport.getAlignment().removeGaps(shifts=new ShiftList());
2096     // edit.alColumnChanges=shifts.getInverse();
2097     // if (viewport.hasHiddenColumns)
2098     // viewport.getColumnSelection().compensateForEdits(shifts);
2099     viewport.setStartRes(seq.findIndex(startRes) - 1);
2100     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2101             .getSequences());
2102
2103   }
2104
2105   /**
2106    * DOCUMENT ME!
2107    * 
2108    * @param e
2109    *                DOCUMENT ME!
2110    */
2111   public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
2112   {
2113     int start = 0, end = viewport.alignment.getWidth() - 1;
2114
2115     SequenceI[] seqs;
2116     if (viewport.getSelectionGroup() != null)
2117     {
2118       seqs = viewport.getSelectionGroup().getSequencesAsArray(
2119               viewport.hiddenRepSequences);
2120       start = viewport.getSelectionGroup().getStartRes();
2121       end = viewport.getSelectionGroup().getEndRes();
2122     }
2123     else
2124     {
2125       seqs = viewport.alignment.getSequencesArray();
2126     }
2127
2128     // This is to maintain viewport position on first residue
2129     // of first sequence
2130     SequenceI seq = viewport.alignment.getSequenceAt(0);
2131     int startRes = seq.findPosition(viewport.startRes);
2132
2133     addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
2134             viewport.alignment));
2135
2136     viewport.setStartRes(seq.findIndex(startRes) - 1);
2137
2138     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2139             .getSequences());
2140
2141   }
2142
2143   /**
2144    * DOCUMENT ME!
2145    * 
2146    * @param e
2147    *                DOCUMENT ME!
2148    */
2149   public void padGapsMenuitem_actionPerformed(ActionEvent e)
2150   {
2151     viewport.padGaps = padGapsMenuitem.isSelected();
2152
2153     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
2154             .getSequences());
2155   }
2156
2157   /**
2158    * DOCUMENT ME!
2159    * 
2160    * @param e
2161    *                DOCUMENT ME!
2162    */
2163   public void findMenuItem_actionPerformed(ActionEvent e)
2164   {
2165     new Finder();
2166   }
2167
2168   public void newView_actionPerformed(ActionEvent e)
2169   {
2170     AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
2171             true);
2172
2173     newap.av.gatherViewsHere = false;
2174
2175     if (viewport.viewName == null)
2176     {
2177       viewport.viewName = "Original";
2178     }
2179
2180     newap.av.historyList = viewport.historyList;
2181     newap.av.redoList = viewport.redoList;
2182
2183     int index = Desktop.getViewCount(viewport.getSequenceSetId());
2184     String newViewName = "View " + index;
2185
2186     Vector comps = (Vector) PaintRefresher.components.get(viewport
2187             .getSequenceSetId());
2188     Vector existingNames = new Vector();
2189     for (int i = 0; i < comps.size(); i++)
2190     {
2191       if (comps.elementAt(i) instanceof AlignmentPanel)
2192       {
2193         AlignmentPanel ap = (AlignmentPanel) comps.elementAt(i);
2194         if (!existingNames.contains(ap.av.viewName))
2195         {
2196           existingNames.addElement(ap.av.viewName);
2197         }
2198       }
2199     }
2200
2201     while (existingNames.contains(newViewName))
2202     {
2203       newViewName = "View " + (++index);
2204     }
2205
2206     newap.av.viewName = newViewName;
2207
2208     addAlignmentPanel(newap, true);
2209
2210     if (alignPanels.size() == 2)
2211     {
2212       viewport.gatherViewsHere = true;
2213     }
2214     tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
2215   }
2216
2217   public void expandViews_actionPerformed(ActionEvent e)
2218   {
2219     Desktop.instance.explodeViews(this);
2220   }
2221
2222   public void gatherViews_actionPerformed(ActionEvent e)
2223   {
2224     Desktop.instance.gatherViews(this);
2225   }
2226
2227   /**
2228    * DOCUMENT ME!
2229    * 
2230    * @param e
2231    *                DOCUMENT ME!
2232    */
2233   public void font_actionPerformed(ActionEvent e)
2234   {
2235     new FontChooser(alignPanel);
2236   }
2237
2238   /**
2239    * DOCUMENT ME!
2240    * 
2241    * @param e
2242    *                DOCUMENT ME!
2243    */
2244   protected void seqLimit_actionPerformed(ActionEvent e)
2245   {
2246     viewport.setShowJVSuffix(seqLimits.isSelected());
2247
2248     alignPanel.idPanel.idCanvas.setPreferredSize(alignPanel
2249             .calculateIdWidth());
2250     alignPanel.paintAlignment(true);
2251   }
2252
2253   public void idRightAlign_actionPerformed(ActionEvent e)
2254   {
2255     viewport.rightAlignIds = idRightAlign.isSelected();
2256     alignPanel.paintAlignment(true);
2257   }
2258
2259   public void centreColumnLabels_actionPerformed(ActionEvent e)
2260   {
2261     viewport.centreColumnLabels = centreColumnLabelsMenuItem.getState();
2262     alignPanel.paintAlignment(true);
2263   }
2264   /* (non-Javadoc)
2265    * @see jalview.jbgui.GAlignFrame#followHighlight_actionPerformed()
2266    */
2267   protected void followHighlight_actionPerformed()
2268   {
2269     if (viewport.followHighlight = this.followHighlightMenuItem.getState())
2270     {
2271       alignPanel.scrollToPosition(alignPanel.seqPanel.seqCanvas.searchResults, false);
2272     }
2273   }
2274
2275   /**
2276    * DOCUMENT ME!
2277    * 
2278    * @param e
2279    *                DOCUMENT ME!
2280    */
2281   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
2282   {
2283     viewport.setColourText(colourTextMenuItem.isSelected());
2284     alignPanel.paintAlignment(true);
2285   }
2286
2287   /**
2288    * DOCUMENT ME!
2289    * 
2290    * @param e
2291    *                DOCUMENT ME!
2292    */
2293   public void wrapMenuItem_actionPerformed(ActionEvent e)
2294   {
2295     scaleAbove.setVisible(wrapMenuItem.isSelected());
2296     scaleLeft.setVisible(wrapMenuItem.isSelected());
2297     scaleRight.setVisible(wrapMenuItem.isSelected());
2298     viewport.setWrapAlignment(wrapMenuItem.isSelected());
2299     alignPanel.setWrapAlignment(wrapMenuItem.isSelected());
2300   }
2301
2302   public void showAllSeqs_actionPerformed(ActionEvent e)
2303   {
2304     viewport.showAllHiddenSeqs();
2305   }
2306
2307   public void showAllColumns_actionPerformed(ActionEvent e)
2308   {
2309     viewport.showAllHiddenColumns();
2310     repaint();
2311   }
2312
2313   public void hideSelSequences_actionPerformed(ActionEvent e)
2314   {
2315     viewport.hideAllSelectedSeqs();
2316     alignPanel.paintAlignment(true);
2317   }
2318
2319   public void hideSelColumns_actionPerformed(ActionEvent e)
2320   {
2321     viewport.hideSelectedColumns();
2322     alignPanel.paintAlignment(true);
2323   }
2324
2325   public void hiddenMarkers_actionPerformed(ActionEvent e)
2326   {
2327     viewport.setShowHiddenMarkers(hiddenMarkers.isSelected());
2328     repaint();
2329   }
2330
2331   /**
2332    * DOCUMENT ME!
2333    * 
2334    * @param e
2335    *                DOCUMENT ME!
2336    */
2337   protected void scaleAbove_actionPerformed(ActionEvent e)
2338   {
2339     viewport.setScaleAboveWrapped(scaleAbove.isSelected());
2340     alignPanel.paintAlignment(true);
2341   }
2342
2343   /**
2344    * DOCUMENT ME!
2345    * 
2346    * @param e
2347    *                DOCUMENT ME!
2348    */
2349   protected void scaleLeft_actionPerformed(ActionEvent e)
2350   {
2351     viewport.setScaleLeftWrapped(scaleLeft.isSelected());
2352     alignPanel.paintAlignment(true);
2353   }
2354
2355   /**
2356    * DOCUMENT ME!
2357    * 
2358    * @param e
2359    *                DOCUMENT ME!
2360    */
2361   protected void scaleRight_actionPerformed(ActionEvent e)
2362   {
2363     viewport.setScaleRightWrapped(scaleRight.isSelected());
2364     alignPanel.paintAlignment(true);
2365   }
2366
2367   /**
2368    * DOCUMENT ME!
2369    * 
2370    * @param e
2371    *                DOCUMENT ME!
2372    */
2373   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
2374   {
2375     viewport.setShowBoxes(viewBoxesMenuItem.isSelected());
2376     alignPanel.paintAlignment(true);
2377   }
2378
2379   /**
2380    * DOCUMENT ME!
2381    * 
2382    * @param e
2383    *                DOCUMENT ME!
2384    */
2385   public void viewTextMenuItem_actionPerformed(ActionEvent e)
2386   {
2387     viewport.setShowText(viewTextMenuItem.isSelected());
2388     alignPanel.paintAlignment(true);
2389   }
2390
2391   /**
2392    * DOCUMENT ME!
2393    * 
2394    * @param e
2395    *                DOCUMENT ME!
2396    */
2397   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
2398   {
2399     viewport.setRenderGaps(renderGapsMenuItem.isSelected());
2400     alignPanel.paintAlignment(true);
2401   }
2402
2403   public FeatureSettings featureSettings;
2404
2405   public void featureSettings_actionPerformed(ActionEvent e)
2406   {
2407     if (featureSettings != null)
2408     {
2409       featureSettings.close();
2410       featureSettings = null;
2411     }
2412     featureSettings = new FeatureSettings(this);
2413   }
2414
2415   /**
2416    * DOCUMENT ME!
2417    * 
2418    * @param evt
2419    *                DOCUMENT ME!
2420    */
2421   public void showSeqFeatures_actionPerformed(ActionEvent evt)
2422   {
2423     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
2424     alignPanel.paintAlignment(true);
2425     if (alignPanel.getOverviewPanel() != null)
2426     {
2427       alignPanel.getOverviewPanel().updateOverviewImage();
2428     }
2429   }
2430
2431   /**
2432    * DOCUMENT ME!
2433    * 
2434    * @param e
2435    *                DOCUMENT ME!
2436    */
2437   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
2438   {
2439     viewport.setShowAnnotation(annotationPanelMenuItem.isSelected());
2440     alignPanel.setAnnotationVisible(annotationPanelMenuItem.isSelected());
2441   }
2442
2443   public void alignmentProperties()
2444   {
2445     JEditorPane editPane = new JEditorPane("text/html", "");
2446     editPane.setEditable(false);
2447     StringBuffer contents = new StringBuffer("<html>");
2448
2449     float avg = 0;
2450     int min = Integer.MAX_VALUE, max = 0;
2451     for (int i = 0; i < viewport.alignment.getHeight(); i++)
2452     {
2453       int size = viewport.alignment.getSequenceAt(i).getEnd()
2454               - viewport.alignment.getSequenceAt(i).getStart();
2455       avg += size;
2456       if (size > max)
2457         max = size;
2458       if (size < min)
2459         min = size;
2460     }
2461     avg = avg / (float) viewport.alignment.getHeight();
2462
2463     contents.append("<br>Sequences: " + viewport.alignment.getHeight());
2464     contents.append("<br>Minimum Sequence Length: " + min);
2465     contents.append("<br>Maximum Sequence Length: " + max);
2466     contents.append("<br>Average Length: " + (int) avg);
2467
2468     if (((Alignment) viewport.alignment).getProperties() != null)
2469     {
2470       Hashtable props = ((Alignment) viewport.alignment).getProperties();
2471       Enumeration en = props.keys();
2472       contents.append("<br><br><table border=\"1\">");
2473       while (en.hasMoreElements())
2474       {
2475         String key = en.nextElement().toString();
2476         StringBuffer val = new StringBuffer();
2477         String vals = props.get(key).toString();
2478         int pos = 0, npos;
2479         do
2480         {
2481           npos = vals.indexOf("\n", pos);
2482           if (npos == -1)
2483           {
2484             val.append(vals.substring(pos));
2485           }
2486           else
2487           {
2488             val.append(vals.substring(pos, npos));
2489             val.append("<br>");
2490           }
2491           pos = npos + 1;
2492         } while (npos != -1);
2493         contents
2494                 .append("<tr><td>" + key + "</td><td>" + val + "</td></tr>");
2495       }
2496       contents.append("</table>");
2497     }
2498     editPane.setText(contents.toString() + "</html>");
2499     JInternalFrame frame = new JInternalFrame();
2500     frame.getContentPane().add(new JScrollPane(editPane));
2501
2502     Desktop.instance.addInternalFrame(frame, "Alignment Properties: "
2503             + getTitle(), 500, 400);
2504   }
2505
2506   /**
2507    * DOCUMENT ME!
2508    * 
2509    * @param e
2510    *                DOCUMENT ME!
2511    */
2512   public void overviewMenuItem_actionPerformed(ActionEvent e)
2513   {
2514     if (alignPanel.overviewPanel != null)
2515     {
2516       return;
2517     }
2518
2519     JInternalFrame frame = new JInternalFrame();
2520     OverviewPanel overview = new OverviewPanel(alignPanel);
2521     frame.setContentPane(overview);
2522     Desktop.addInternalFrame(frame, "Overview " + this.getTitle(), frame
2523             .getWidth(), frame.getHeight());
2524     frame.pack();
2525     frame.setLayer(JLayeredPane.PALETTE_LAYER);
2526     frame
2527             .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
2528             {
2529               public void internalFrameClosed(
2530                       javax.swing.event.InternalFrameEvent evt)
2531               {
2532                 alignPanel.setOverviewPanel(null);
2533               };
2534             });
2535
2536     alignPanel.setOverviewPanel(overview);
2537   }
2538
2539   public void textColour_actionPerformed(ActionEvent e)
2540   {
2541     new TextColourChooser().chooseColour(alignPanel, null);
2542   }
2543
2544   /**
2545    * DOCUMENT ME!
2546    * 
2547    * @param e
2548    *                DOCUMENT ME!
2549    */
2550   protected void noColourmenuItem_actionPerformed(ActionEvent e)
2551   {
2552     changeColour(null);
2553   }
2554
2555   /**
2556    * DOCUMENT ME!
2557    * 
2558    * @param e
2559    *                DOCUMENT ME!
2560    */
2561   public void clustalColour_actionPerformed(ActionEvent e)
2562   {
2563     changeColour(new ClustalxColourScheme(
2564             viewport.alignment.getSequences(), viewport.alignment
2565                     .getWidth()));
2566   }
2567
2568   /**
2569    * DOCUMENT ME!
2570    * 
2571    * @param e
2572    *                DOCUMENT ME!
2573    */
2574   public void zappoColour_actionPerformed(ActionEvent e)
2575   {
2576     changeColour(new ZappoColourScheme());
2577   }
2578
2579   /**
2580    * DOCUMENT ME!
2581    * 
2582    * @param e
2583    *                DOCUMENT ME!
2584    */
2585   public void taylorColour_actionPerformed(ActionEvent e)
2586   {
2587     changeColour(new TaylorColourScheme());
2588   }
2589
2590   /**
2591    * DOCUMENT ME!
2592    * 
2593    * @param e
2594    *                DOCUMENT ME!
2595    */
2596   public void hydrophobicityColour_actionPerformed(ActionEvent e)
2597   {
2598     changeColour(new HydrophobicColourScheme());
2599   }
2600
2601   /**
2602    * DOCUMENT ME!
2603    * 
2604    * @param e
2605    *                DOCUMENT ME!
2606    */
2607   public void helixColour_actionPerformed(ActionEvent e)
2608   {
2609     changeColour(new HelixColourScheme());
2610   }
2611
2612   /**
2613    * DOCUMENT ME!
2614    * 
2615    * @param e
2616    *                DOCUMENT ME!
2617    */
2618   public void strandColour_actionPerformed(ActionEvent e)
2619   {
2620     changeColour(new StrandColourScheme());
2621   }
2622
2623   /**
2624    * DOCUMENT ME!
2625    * 
2626    * @param e
2627    *                DOCUMENT ME!
2628    */
2629   public void turnColour_actionPerformed(ActionEvent e)
2630   {
2631     changeColour(new TurnColourScheme());
2632   }
2633
2634   /**
2635    * DOCUMENT ME!
2636    * 
2637    * @param e
2638    *                DOCUMENT ME!
2639    */
2640   public void buriedColour_actionPerformed(ActionEvent e)
2641   {
2642     changeColour(new BuriedColourScheme());
2643   }
2644
2645   /**
2646    * DOCUMENT ME!
2647    * 
2648    * @param e
2649    *                DOCUMENT ME!
2650    */
2651   public void nucleotideColour_actionPerformed(ActionEvent e)
2652   {
2653     changeColour(new NucleotideColourScheme());
2654   }
2655
2656   public void annotationColour_actionPerformed(ActionEvent e)
2657   {
2658     new AnnotationColourChooser(viewport, alignPanel);
2659   }
2660
2661   /**
2662    * DOCUMENT ME!
2663    * 
2664    * @param e
2665    *                DOCUMENT ME!
2666    */
2667   protected void applyToAllGroups_actionPerformed(ActionEvent e)
2668   {
2669     viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
2670   }
2671
2672   /**
2673    * DOCUMENT ME!
2674    * 
2675    * @param cs
2676    *                DOCUMENT ME!
2677    */
2678   public void changeColour(ColourSchemeI cs)
2679   {
2680     int threshold = 0;
2681
2682     if (cs != null)
2683     {
2684       if (viewport.getAbovePIDThreshold())
2685       {
2686         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
2687                 "Background");
2688
2689         cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
2690
2691         viewport.setGlobalColourScheme(cs);
2692       }
2693       else
2694       {
2695         cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
2696       }
2697
2698       if (viewport.getConservationSelected())
2699       {
2700
2701         Alignment al = (Alignment) viewport.alignment;
2702         Conservation c = new Conservation("All",
2703                 ResidueProperties.propHash, 3, al.getSequences(), 0, al
2704                         .getWidth() - 1);
2705
2706         c.calculate();
2707         c.verdict(false, viewport.ConsPercGaps);
2708
2709         cs.setConservation(c);
2710
2711         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel,
2712                 cs, "Background"));
2713       }
2714       else
2715       {
2716         cs.setConservation(null);
2717       }
2718
2719       cs.setConsensus(viewport.hconsensus);
2720     }
2721
2722     viewport.setGlobalColourScheme(cs);
2723
2724     if (viewport.getColourAppliesToAllGroups())
2725     {
2726       Vector groups = viewport.alignment.getGroups();
2727
2728       for (int i = 0; i < groups.size(); i++)
2729       {
2730         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
2731
2732         if (cs == null)
2733         {
2734           sg.cs = null;
2735           continue;
2736         }
2737
2738         if (cs instanceof ClustalxColourScheme)
2739         {
2740           sg.cs = new ClustalxColourScheme(sg
2741                   .getSequences(viewport.hiddenRepSequences), sg.getWidth());
2742         }
2743         else if (cs instanceof UserColourScheme)
2744         {
2745           sg.cs = new UserColourScheme(((UserColourScheme) cs).getColours());
2746         }
2747         else
2748         {
2749           try
2750           {
2751             sg.cs = (ColourSchemeI) cs.getClass().newInstance();
2752           } catch (Exception ex)
2753           {
2754           }
2755         }
2756
2757         if (viewport.getAbovePIDThreshold()
2758                 || cs instanceof PIDColourScheme
2759                 || cs instanceof Blosum62ColourScheme)
2760         {
2761           sg.cs.setThreshold(threshold, viewport.getIgnoreGapsConsensus());
2762
2763           sg.cs.setConsensus(AAFrequency.calculate(sg
2764                   .getSequences(viewport.hiddenRepSequences), sg
2765                   .getStartRes(), sg.getEndRes() + 1));
2766         }
2767         else
2768         {
2769           sg.cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
2770         }
2771
2772         if (viewport.getConservationSelected())
2773         {
2774           Conservation c = new Conservation("Group",
2775                   ResidueProperties.propHash, 3, sg
2776                           .getSequences(viewport.hiddenRepSequences), sg
2777                           .getStartRes(), sg.getEndRes() + 1);
2778           c.calculate();
2779           c.verdict(false, viewport.ConsPercGaps);
2780           sg.cs.setConservation(c);
2781         }
2782         else
2783         {
2784           sg.cs.setConservation(null);
2785         }
2786       }
2787     }
2788
2789     if (alignPanel.getOverviewPanel() != null)
2790     {
2791       alignPanel.getOverviewPanel().updateOverviewImage();
2792     }
2793
2794     alignPanel.paintAlignment(true);
2795   }
2796
2797   /**
2798    * DOCUMENT ME!
2799    * 
2800    * @param e
2801    *                DOCUMENT ME!
2802    */
2803   protected void modifyPID_actionPerformed(ActionEvent e)
2804   {
2805     if (viewport.getAbovePIDThreshold()
2806             && viewport.globalColourScheme != null)
2807     {
2808       SliderPanel.setPIDSliderSource(alignPanel, viewport
2809               .getGlobalColourScheme(), "Background");
2810       SliderPanel.showPIDSlider();
2811     }
2812   }
2813
2814   /**
2815    * DOCUMENT ME!
2816    * 
2817    * @param e
2818    *                DOCUMENT ME!
2819    */
2820   protected void modifyConservation_actionPerformed(ActionEvent e)
2821   {
2822     if (viewport.getConservationSelected()
2823             && viewport.globalColourScheme != null)
2824     {
2825       SliderPanel.setConservationSlider(alignPanel,
2826               viewport.globalColourScheme, "Background");
2827       SliderPanel.showConservationSlider();
2828     }
2829   }
2830
2831   /**
2832    * DOCUMENT ME!
2833    * 
2834    * @param e
2835    *                DOCUMENT ME!
2836    */
2837   protected void conservationMenuItem_actionPerformed(ActionEvent e)
2838   {
2839     viewport.setConservationSelected(conservationMenuItem.isSelected());
2840
2841     viewport.setAbovePIDThreshold(false);
2842     abovePIDThreshold.setSelected(false);
2843
2844     changeColour(viewport.getGlobalColourScheme());
2845
2846     modifyConservation_actionPerformed(null);
2847   }
2848
2849   /**
2850    * DOCUMENT ME!
2851    * 
2852    * @param e
2853    *                DOCUMENT ME!
2854    */
2855   public void abovePIDThreshold_actionPerformed(ActionEvent e)
2856   {
2857     viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
2858
2859     conservationMenuItem.setSelected(false);
2860     viewport.setConservationSelected(false);
2861
2862     changeColour(viewport.getGlobalColourScheme());
2863
2864     modifyPID_actionPerformed(null);
2865   }
2866
2867   /**
2868    * DOCUMENT ME!
2869    * 
2870    * @param e
2871    *                DOCUMENT ME!
2872    */
2873   public void userDefinedColour_actionPerformed(ActionEvent e)
2874   {
2875     if (e.getActionCommand().equals("User Defined..."))
2876     {
2877       new UserDefinedColours(alignPanel, null);
2878     }
2879     else
2880     {
2881       UserColourScheme udc = (UserColourScheme) UserDefinedColours
2882               .getUserColourSchemes().get(e.getActionCommand());
2883
2884       changeColour(udc);
2885     }
2886   }
2887
2888   public void updateUserColourMenu()
2889   {
2890
2891     Component[] menuItems = colourMenu.getMenuComponents();
2892     int i, iSize = menuItems.length;
2893     for (i = 0; i < iSize; i++)
2894     {
2895       if (menuItems[i].getName() != null
2896               && menuItems[i].getName().equals("USER_DEFINED"))
2897       {
2898         colourMenu.remove(menuItems[i]);
2899         iSize--;
2900       }
2901     }
2902     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
2903     {
2904       java.util.Enumeration userColours = jalview.gui.UserDefinedColours
2905               .getUserColourSchemes().keys();
2906
2907       while (userColours.hasMoreElements())
2908       {
2909         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
2910                 userColours.nextElement().toString());
2911         radioItem.setName("USER_DEFINED");
2912         radioItem.addMouseListener(new MouseAdapter()
2913         {
2914           public void mousePressed(MouseEvent evt)
2915           {
2916             if (evt.isControlDown()
2917                     || SwingUtilities.isRightMouseButton(evt))
2918             {
2919               radioItem
2920                       .removeActionListener(radioItem.getActionListeners()[0]);
2921
2922               int option = JOptionPane.showInternalConfirmDialog(
2923                       jalview.gui.Desktop.desktop,
2924                       "Remove from default list?",
2925                       "Remove user defined colour",
2926                       JOptionPane.YES_NO_OPTION);
2927               if (option == JOptionPane.YES_OPTION)
2928               {
2929                 jalview.gui.UserDefinedColours
2930                         .removeColourFromDefaults(radioItem.getText());
2931                 colourMenu.remove(radioItem);
2932               }
2933               else
2934               {
2935                 radioItem.addActionListener(new ActionListener()
2936                 {
2937                   public void actionPerformed(ActionEvent evt)
2938                   {
2939                     userDefinedColour_actionPerformed(evt);
2940                   }
2941                 });
2942               }
2943             }
2944           }
2945         });
2946         radioItem.addActionListener(new ActionListener()
2947         {
2948           public void actionPerformed(ActionEvent evt)
2949           {
2950             userDefinedColour_actionPerformed(evt);
2951           }
2952         });
2953
2954         colourMenu.insert(radioItem, 15);
2955         colours.add(radioItem);
2956       }
2957     }
2958   }
2959
2960   /**
2961    * DOCUMENT ME!
2962    * 
2963    * @param e
2964    *                DOCUMENT ME!
2965    */
2966   public void PIDColour_actionPerformed(ActionEvent e)
2967   {
2968     changeColour(new PIDColourScheme());
2969   }
2970
2971   /**
2972    * DOCUMENT ME!
2973    * 
2974    * @param e
2975    *                DOCUMENT ME!
2976    */
2977   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
2978   {
2979     changeColour(new Blosum62ColourScheme());
2980   }
2981
2982   /**
2983    * DOCUMENT ME!
2984    * 
2985    * @param e
2986    *                DOCUMENT ME!
2987    */
2988   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
2989   {
2990     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
2991     AlignmentSorter.sortByPID(viewport.getAlignment(), viewport
2992             .getAlignment().getSequenceAt(0), null);
2993     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
2994             viewport.alignment));
2995     alignPanel.paintAlignment(true);
2996   }
2997
2998   /**
2999    * DOCUMENT ME!
3000    * 
3001    * @param e
3002    *                DOCUMENT ME!
3003    */
3004   public void sortIDMenuItem_actionPerformed(ActionEvent e)
3005   {
3006     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3007     AlignmentSorter.sortByID(viewport.getAlignment());
3008     addHistoryItem(new OrderCommand("ID Sort", oldOrder, viewport.alignment));
3009     alignPanel.paintAlignment(true);
3010   }
3011
3012   /**
3013    * DOCUMENT ME!
3014    * 
3015    * @param e
3016    *                DOCUMENT ME!
3017    */
3018   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
3019   {
3020     SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3021     AlignmentSorter.sortByGroup(viewport.getAlignment());
3022     addHistoryItem(new OrderCommand("Group Sort", oldOrder,
3023             viewport.alignment));
3024
3025     alignPanel.paintAlignment(true);
3026   }
3027
3028   /**
3029    * DOCUMENT ME!
3030    * 
3031    * @param e
3032    *                DOCUMENT ME!
3033    */
3034   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
3035   {
3036     new RedundancyPanel(alignPanel, this);
3037   }
3038
3039   /**
3040    * DOCUMENT ME!
3041    * 
3042    * @param e
3043    *                DOCUMENT ME!
3044    */
3045   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
3046   {
3047     if ((viewport.getSelectionGroup() == null)
3048             || (viewport.getSelectionGroup().getSize() < 2))
3049     {
3050       JOptionPane.showInternalMessageDialog(this,
3051               "You must select at least 2 sequences.", "Invalid Selection",
3052               JOptionPane.WARNING_MESSAGE);
3053     }
3054     else
3055     {
3056       JInternalFrame frame = new JInternalFrame();
3057       frame.setContentPane(new PairwiseAlignPanel(viewport));
3058       Desktop.addInternalFrame(frame, "Pairwise Alignment", 600, 500);
3059     }
3060   }
3061
3062   /**
3063    * DOCUMENT ME!
3064    * 
3065    * @param e
3066    *                DOCUMENT ME!
3067    */
3068   public void PCAMenuItem_actionPerformed(ActionEvent e)
3069   {
3070     if (((viewport.getSelectionGroup() != null)
3071             && (viewport.getSelectionGroup().getSize() < 4) && (viewport
3072             .getSelectionGroup().getSize() > 0))
3073             || (viewport.getAlignment().getHeight() < 4))
3074     {
3075       JOptionPane.showInternalMessageDialog(this,
3076               "Principal component analysis must take\n"
3077                       + "at least 4 input sequences.",
3078               "Sequence selection insufficient",
3079               JOptionPane.WARNING_MESSAGE);
3080
3081       return;
3082     }
3083
3084     new PCAPanel(alignPanel);
3085   }
3086
3087   public void autoCalculate_actionPerformed(ActionEvent e)
3088   {
3089     viewport.autoCalculateConsensus = autoCalculate.isSelected();
3090     if (viewport.autoCalculateConsensus)
3091     {
3092       viewport.firePropertyChange("alignment", null, viewport
3093               .getAlignment().getSequences());
3094     }
3095   }
3096
3097   /**
3098    * DOCUMENT ME!
3099    * 
3100    * @param e
3101    *                DOCUMENT ME!
3102    */
3103   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
3104   {
3105     NewTreePanel("AV", "PID", "Average distance tree using PID");
3106   }
3107
3108   /**
3109    * DOCUMENT ME!
3110    * 
3111    * @param e
3112    *                DOCUMENT ME!
3113    */
3114   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
3115   {
3116     NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
3117   }
3118
3119   /**
3120    * DOCUMENT ME!
3121    * 
3122    * @param e
3123    *                DOCUMENT ME!
3124    */
3125   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3126   {
3127     NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
3128   }
3129
3130   /**
3131    * DOCUMENT ME!
3132    * 
3133    * @param e
3134    *                DOCUMENT ME!
3135    */
3136   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
3137   {
3138     NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
3139   }
3140
3141   /**
3142    * DOCUMENT ME!
3143    * 
3144    * @param type
3145    *                DOCUMENT ME!
3146    * @param pwType
3147    *                DOCUMENT ME!
3148    * @param title
3149    *                DOCUMENT ME!
3150    */
3151   void NewTreePanel(String type, String pwType, String title)
3152   {
3153     TreePanel tp;
3154
3155     if (viewport.getSelectionGroup() != null)
3156     {
3157       if (viewport.getSelectionGroup().getSize() < 3)
3158       {
3159         JOptionPane
3160                 .showMessageDialog(
3161                         Desktop.desktop,
3162                         "You need to have more than two sequences selected to build a tree!",
3163                         "Not enough sequences", JOptionPane.WARNING_MESSAGE);
3164         return;
3165       }
3166
3167       int s = 0;
3168       SequenceGroup sg = viewport.getSelectionGroup();
3169
3170       /* Decide if the selection is a column region */
3171       while (s < sg.getSize())
3172       {
3173         if (((SequenceI) sg.getSequences(null).elementAt(s++)).getLength() < sg
3174                 .getEndRes())
3175         {
3176           JOptionPane
3177                   .showMessageDialog(
3178                           Desktop.desktop,
3179                           "The selected region to create a tree may\nonly contain residues or gaps.\n"
3180                                   + "Try using the Pad function in the edit menu,\n"
3181                                   + "or one of the multiple sequence alignment web services.",
3182                           "Sequences in selection are not aligned",
3183                           JOptionPane.WARNING_MESSAGE);
3184
3185           return;
3186         }
3187       }
3188
3189       title = title + " on region";
3190       tp = new TreePanel(alignPanel, type, pwType);
3191     }
3192     else
3193     {
3194       // are the sequences aligned?
3195       if (!viewport.alignment.isAligned())
3196       {
3197         JOptionPane
3198                 .showMessageDialog(
3199                         Desktop.desktop,
3200                         "The sequences must be aligned before creating a tree.\n"
3201                                 + "Try using the Pad function in the edit menu,\n"
3202                                 + "or one of the multiple sequence alignment web services.",
3203                         "Sequences not aligned",
3204                         JOptionPane.WARNING_MESSAGE);
3205
3206         return;
3207       }
3208
3209       if (viewport.alignment.getHeight() < 2)
3210       {
3211         return;
3212       }
3213
3214       tp = new TreePanel(alignPanel, type, pwType);
3215     }
3216
3217     title += " from ";
3218
3219     if (viewport.viewName != null)
3220     {
3221       title += viewport.viewName + " of ";
3222     }
3223
3224     title += this.title;
3225
3226     Desktop.addInternalFrame(tp, title, 600, 500);
3227   }
3228
3229   /**
3230    * DOCUMENT ME!
3231    * 
3232    * @param title
3233    *                DOCUMENT ME!
3234    * @param order
3235    *                DOCUMENT ME!
3236    */
3237   public void addSortByOrderMenuItem(String title,
3238           final AlignmentOrder order)
3239   {
3240     final JMenuItem item = new JMenuItem("by " + title);
3241     sort.add(item);
3242     item.addActionListener(new java.awt.event.ActionListener()
3243     {
3244       public void actionPerformed(ActionEvent e)
3245       {
3246         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3247
3248         // TODO: JBPNote - have to map order entries to curent SequenceI
3249         // pointers
3250         AlignmentSorter.sortBy(viewport.getAlignment(), order);
3251
3252         addHistoryItem(new OrderCommand(order.getName(), oldOrder,
3253                 viewport.alignment));
3254
3255         alignPanel.paintAlignment(true);
3256       }
3257     });
3258   }
3259
3260   /**
3261    * Add a new sort by annotation score menu item
3262    * 
3263    * @param sort
3264    *                the menu to add the option to
3265    * @param scoreLabel
3266    *                the label used to retrieve scores for each sequence on the
3267    *                alignment
3268    */
3269   public void addSortByAnnotScoreMenuItem(JMenu sort,
3270           final String scoreLabel)
3271   {
3272     final JMenuItem item = new JMenuItem(scoreLabel);
3273     sort.add(item);
3274     item.addActionListener(new java.awt.event.ActionListener()
3275     {
3276       public void actionPerformed(ActionEvent e)
3277       {
3278         SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3279         AlignmentSorter.sortByAnnotationScore(scoreLabel, viewport
3280                 .getAlignment());// ,viewport.getSelectionGroup());
3281         addHistoryItem(new OrderCommand("Sort by " + scoreLabel, oldOrder,
3282                 viewport.alignment));
3283         alignPanel.paintAlignment(true);
3284       }
3285     });
3286   }
3287
3288   /**
3289    * last hash for alignment's annotation array - used to minimise cost of
3290    * rebuild.
3291    */
3292   protected int _annotationScoreVectorHash;
3293
3294   /**
3295    * search the alignment and rebuild the sort by annotation score submenu the
3296    * last alignment annotation vector hash is stored to minimize cost of
3297    * rebuilding in subsequence calls.
3298    * 
3299    */
3300   public void buildSortByAnnotationScoresMenu()
3301   {
3302     if (viewport.alignment.getAlignmentAnnotation() == null)
3303     {
3304       return;
3305     }
3306
3307     if (viewport.alignment.getAlignmentAnnotation().hashCode() != _annotationScoreVectorHash)
3308     {
3309       sortByAnnotScore.removeAll();
3310       // almost certainly a quicker way to do this - but we keep it simple
3311       Hashtable scoreSorts = new Hashtable();
3312       AlignmentAnnotation aann[];
3313       Enumeration sq = viewport.alignment.getSequences().elements();
3314       while (sq.hasMoreElements())
3315       {
3316         aann = ((SequenceI) sq.nextElement()).getAnnotation();
3317         for (int i = 0; aann != null && i < aann.length; i++)
3318         {
3319           if (aann[i].hasScore() && aann[i].sequenceRef != null)
3320           {
3321             scoreSorts.put(aann[i].label, aann[i].label);
3322           }
3323         }
3324       }
3325       Enumeration labels = scoreSorts.keys();
3326       while (labels.hasMoreElements())
3327       {
3328         addSortByAnnotScoreMenuItem(sortByAnnotScore, (String) labels
3329                 .nextElement());
3330       }
3331       sortByAnnotScore.setVisible(scoreSorts.size() > 0);
3332       scoreSorts.clear();
3333
3334       _annotationScoreVectorHash = viewport.alignment
3335               .getAlignmentAnnotation().hashCode();
3336     }
3337   }
3338
3339   /**
3340    * Maintain the Order by->Displayed Tree menu. Creates a new menu item for a
3341    * TreePanel with an appropriate <code>jalview.analysis.AlignmentSorter</code>
3342    * call. Listeners are added to remove the menu item when the treePanel is
3343    * closed, and adjust the tree leaf to sequence mapping when the alignment is
3344    * modified.
3345    * 
3346    * @param treePanel
3347    *                Displayed tree window.
3348    * @param title
3349    *                SortBy menu item title.
3350    */
3351   public void buildTreeMenu()
3352   {
3353     sortByTreeMenu.removeAll();
3354
3355     Vector comps = (Vector) PaintRefresher.components.get(viewport
3356             .getSequenceSetId());
3357     Vector treePanels = new Vector();
3358     int i, iSize = comps.size();
3359     for (i = 0; i < iSize; i++)
3360     {
3361       if (comps.elementAt(i) instanceof TreePanel)
3362       {
3363         treePanels.add(comps.elementAt(i));
3364       }
3365     }
3366
3367     iSize = treePanels.size();
3368
3369     if (iSize < 1)
3370     {
3371       sortByTreeMenu.setVisible(false);
3372       return;
3373     }
3374
3375     sortByTreeMenu.setVisible(true);
3376
3377     for (i = 0; i < treePanels.size(); i++)
3378     {
3379       TreePanel tp = (TreePanel) treePanels.elementAt(i);
3380       final JMenuItem item = new JMenuItem(tp.getTitle());
3381       final NJTree tree = ((TreePanel) treePanels.elementAt(i)).getTree();
3382       item.addActionListener(new java.awt.event.ActionListener()
3383       {
3384         public void actionPerformed(ActionEvent e)
3385         {
3386           SequenceI[] oldOrder = viewport.getAlignment()
3387                   .getSequencesArray();
3388           AlignmentSorter.sortByTree(viewport.getAlignment(), tree);
3389
3390           addHistoryItem(new OrderCommand("Tree Sort", oldOrder,
3391                   viewport.alignment));
3392
3393           alignPanel.paintAlignment(true);
3394         }
3395       });
3396
3397       sortByTreeMenu.add(item);
3398     }
3399   }
3400
3401   /**
3402    * Work out whether the whole set of sequences or just the selected set will
3403    * be submitted for multiple alignment.
3404    * 
3405    */
3406   public jalview.datamodel.AlignmentView gatherSequencesForAlignment()
3407   {
3408     // Now, check we have enough sequences
3409     AlignmentView msa = null;
3410
3411     if ((viewport.getSelectionGroup() != null)
3412             && (viewport.getSelectionGroup().getSize() > 1))
3413     {
3414       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to
3415       // some common interface!
3416       /*
3417        * SequenceGroup seqs = viewport.getSelectionGroup(); int sz; msa = new
3418        * SequenceI[sz = seqs.getSize(false)];
3419        * 
3420        * for (int i = 0; i < sz; i++) { msa[i] = (SequenceI)
3421        * seqs.getSequenceAt(i); }
3422        */
3423       msa = viewport.getAlignmentView(true);
3424     }
3425     else
3426     {
3427       /*
3428        * Vector seqs = viewport.getAlignment().getSequences();
3429        * 
3430        * if (seqs.size() > 1) { msa = new SequenceI[seqs.size()];
3431        * 
3432        * for (int i = 0; i < seqs.size(); i++) { msa[i] = (SequenceI)
3433        * seqs.elementAt(i); } }
3434        */
3435       msa = viewport.getAlignmentView(false);
3436     }
3437     return msa;
3438   }
3439
3440   /**
3441    * Decides what is submitted to a secondary structure prediction service: the
3442    * first sequence in the alignment, or in the current selection, or, if the
3443    * alignment is 'aligned' (ie padded with gaps), then the currently selected
3444    * region or the whole alignment. (where the first sequence in the set is the
3445    * one that the prediction will be for).
3446    */
3447   public AlignmentView gatherSeqOrMsaForSecStrPrediction()
3448   {
3449     AlignmentView seqs = null;
3450
3451     if ((viewport.getSelectionGroup() != null)
3452             && (viewport.getSelectionGroup().getSize() > 0))
3453     {
3454       seqs = viewport.getAlignmentView(true);
3455     }
3456     else
3457     {
3458       seqs = viewport.getAlignmentView(false);
3459     }
3460     // limit sequences - JBPNote in future - could spawn multiple prediction
3461     // jobs
3462     // TODO: viewport.alignment.isAligned is a global state - the local
3463     // selection may well be aligned - we preserve 2.0.8 behaviour for moment.
3464     if (!viewport.alignment.isAligned())
3465     {
3466       seqs.setSequences(new SeqCigar[]
3467       { seqs.getSequences()[0] });
3468     }
3469     return seqs;
3470   }
3471
3472   /**
3473    * DOCUMENT ME!
3474    * 
3475    * @param e
3476    *                DOCUMENT ME!
3477    */
3478   protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
3479   {
3480     // Pick the tree file
3481     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
3482             .getProperty("LAST_DIRECTORY"));
3483     chooser.setFileView(new JalviewFileView());
3484     chooser.setDialogTitle("Select a newick-like tree file");
3485     chooser.setToolTipText("Load a tree file");
3486
3487     int value = chooser.showOpenDialog(null);
3488
3489     if (value == JalviewFileChooser.APPROVE_OPTION)
3490     {
3491       String choice = chooser.getSelectedFile().getPath();
3492       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
3493       jalview.io.NewickFile fin = null;
3494       try
3495       {
3496         fin = new jalview.io.NewickFile(choice, "File");
3497         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
3498       } catch (Exception ex)
3499       {
3500         JOptionPane.showMessageDialog(Desktop.desktop, ex.getMessage(),
3501                 "Problem reading tree file", JOptionPane.WARNING_MESSAGE);
3502         ex.printStackTrace();
3503       }
3504       if (fin != null && fin.hasWarningMessage())
3505       {
3506         JOptionPane.showMessageDialog(Desktop.desktop, fin
3507                 .getWarningMessage(), "Possible problem with tree file",
3508                 JOptionPane.WARNING_MESSAGE);
3509       }
3510     }
3511   }
3512
3513   public TreePanel ShowNewickTree(NewickFile nf, String title)
3514   {
3515     return ShowNewickTree(nf, title, 600, 500, 4, 5);
3516   }
3517
3518   public TreePanel ShowNewickTree(NewickFile nf, String title,
3519           AlignmentView input)
3520   {
3521     return ShowNewickTree(nf, title, input, 600, 500, 4, 5);
3522   }
3523
3524   public TreePanel ShowNewickTree(NewickFile nf, String title, int w,
3525           int h, int x, int y)
3526   {
3527     return ShowNewickTree(nf, title, null, w, h, x, y);
3528   }
3529
3530   /**
3531    * Add a treeviewer for the tree extracted from a newick file object to the
3532    * current alignment view
3533    * 
3534    * @param nf
3535    *                the tree
3536    * @param title
3537    *                tree viewer title
3538    * @param input
3539    *                Associated alignment input data (or null)
3540    * @param w
3541    *                width
3542    * @param h
3543    *                height
3544    * @param x
3545    *                position
3546    * @param y
3547    *                position
3548    * @return TreePanel handle
3549    */
3550   public TreePanel ShowNewickTree(NewickFile nf, String title,
3551           AlignmentView input, int w, int h, int x, int y)
3552   {
3553     TreePanel tp = null;
3554
3555     try
3556     {
3557       nf.parse();
3558
3559       if (nf.getTree() != null)
3560       {
3561         tp = new TreePanel(alignPanel, "FromFile", title, nf, input);
3562
3563         tp.setSize(w, h);
3564
3565         if (x > 0 && y > 0)
3566         {
3567           tp.setLocation(x, y);
3568         }
3569
3570         Desktop.addInternalFrame(tp, title, w, h);
3571       }
3572     } catch (Exception ex)
3573     {
3574       ex.printStackTrace();
3575     }
3576
3577     return tp;
3578   }
3579
3580   /**
3581    * Generates menu items and listener event actions for web service clients
3582    * 
3583    */
3584   public void BuildWebServiceMenu()
3585   {
3586     // TODO: add support for context dependent disabling of services based on
3587     // alignment and current selection
3588     // TODO: add additional serviceHandle parameter to specify abstract handler
3589     // class independently of AbstractName
3590     // TODO: add in rediscovery GUI function to restart discoverer
3591     // TODO: group services by location as well as function and/or introduce
3592     // object broker mechanism.
3593     if ((Discoverer.services != null) && (Discoverer.services.size() > 0))
3594     {
3595       // TODO: refactor to allow list of AbstractName/Handler bindings to be
3596       // stored or retrieved from elsewhere
3597       Vector msaws = (Vector) Discoverer.services.get("MsaWS");
3598       Vector secstrpr = (Vector) Discoverer.services.get("SecStrPred");
3599       Vector seqsrch = (Vector) Discoverer.services.get("SeqSearch");
3600       // TODO: move GUI generation code onto service implementation - so a
3601       // client instance attaches itself to the GUI with method call like
3602       // jalview.ws.MsaWSClient.bind(servicehandle, Desktop.instance,
3603       // alignframe)
3604       Vector wsmenu = new Vector();
3605       final IProgressIndicator af = this;
3606       if (msaws != null)
3607       {
3608         // Add any Multiple Sequence Alignment Services
3609         final JMenu msawsmenu = new JMenu("Alignment");
3610         for (int i = 0, j = msaws.size(); i < j; i++)
3611         {
3612           final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) msaws
3613                   .get(i);
3614           jalview.ws.WSClient impl = jalview.ws.Discoverer
3615                   .getServiceClient(sh);
3616           impl.attachWSMenuEntry(msawsmenu, this);
3617
3618         }
3619         wsmenu.add(msawsmenu);
3620       }
3621       if (secstrpr != null)
3622       {
3623         // Add any secondary structure prediction services
3624         final JMenu secstrmenu = new JMenu("Secondary Structure Prediction");
3625         for (int i = 0, j = secstrpr.size(); i < j; i++)
3626         {
3627           final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) secstrpr
3628                   .get(i);
3629           jalview.ws.WSClient impl = jalview.ws.Discoverer
3630                   .getServiceClient(sh);
3631           impl.attachWSMenuEntry(secstrmenu, this);
3632         }
3633         wsmenu.add(secstrmenu);
3634       }
3635       if (seqsrch != null)
3636       {
3637         // Add any sequence search services
3638         final JMenu seqsrchmenu = new JMenu("Sequence Database Search");
3639         for (int i = 0, j = seqsrch.size(); i < j; i++)
3640         {
3641           final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) seqsrch
3642                   .elementAt(i);
3643           jalview.ws.WSClient impl = jalview.ws.Discoverer
3644                   .getServiceClient(sh);
3645           impl.attachWSMenuEntry(seqsrchmenu, this);
3646         }
3647         // finally, add the whole shebang onto the webservices menu
3648         wsmenu.add(seqsrchmenu);
3649       }
3650       resetWebServiceMenu();
3651       for (int i = 0, j = wsmenu.size(); i < j; i++)
3652       {
3653         webService.add((JMenu) wsmenu.get(i));
3654       }
3655     }
3656     else
3657     {
3658       resetWebServiceMenu();
3659       this.webService.add(this.webServiceNoServices);
3660     }
3661   }
3662
3663   /**
3664    * empty the web service menu and add any ad-hoc functions not dynamically
3665    * discovered.
3666    * 
3667    */
3668   private void resetWebServiceMenu()
3669   {
3670     webService.removeAll();
3671     build_fetchdbmenu(webService);
3672   }
3673
3674   /*
3675    * public void vamsasStore_actionPerformed(ActionEvent e) { JalviewFileChooser
3676    * chooser = new JalviewFileChooser(jalview.bin.Cache.
3677    * getProperty("LAST_DIRECTORY"));
3678    * 
3679    * chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle("Export
3680    * to Vamsas file"); chooser.setToolTipText("Export");
3681    * 
3682    * int value = chooser.showSaveDialog(this);
3683    * 
3684    * if (value == JalviewFileChooser.APPROVE_OPTION) {
3685    * jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
3686    * //vs.store(chooser.getSelectedFile().getAbsolutePath() ); vs.storeJalview(
3687    * chooser.getSelectedFile().getAbsolutePath(), this); } }
3688    */
3689   /**
3690    * prototype of an automatically enabled/disabled analysis function
3691    * 
3692    */
3693   protected void setShowProductsEnabled()
3694   {
3695     SequenceI[] selection = viewport.getSequenceSelection();
3696     if (canShowProducts(selection, viewport.getSelectionGroup() != null,
3697             viewport.getAlignment().getDataset()))
3698     {
3699       showProducts.setEnabled(true);
3700
3701     }
3702     else
3703     {
3704       showProducts.setEnabled(false);
3705     }
3706   }
3707
3708   /**
3709    * search selection for sequence xRef products and build the show products
3710    * menu.
3711    * 
3712    * @param selection
3713    * @param dataset
3714    * @return true if showProducts menu should be enabled.
3715    */
3716   public boolean canShowProducts(SequenceI[] selection,
3717           boolean isRegionSelection, Alignment dataset)
3718   {
3719     boolean showp = false;
3720     try
3721     {
3722       showProducts.removeAll();
3723       final boolean dna = viewport.getAlignment().isNucleotide();
3724       final Alignment ds = dataset;
3725       String[] ptypes = CrossRef.findSequenceXrefTypes(dna, selection,
3726               dataset);
3727       // Object[] prods =
3728       // CrossRef.buildXProductsList(viewport.getAlignment().isNucleotide(),
3729       // selection, dataset, true);
3730       final SequenceI[] sel = selection;
3731       for (int t = 0; ptypes != null && t < ptypes.length; t++)
3732       {
3733         showp = true;
3734         final boolean isRegSel = isRegionSelection;
3735         final AlignFrame af = this;
3736         final String source = ptypes[t];
3737         JMenuItem xtype = new JMenuItem(ptypes[t]);
3738         xtype.addActionListener(new ActionListener()
3739         {
3740
3741           public void actionPerformed(ActionEvent e)
3742           {
3743             // TODO: new thread for this call with vis-delay
3744             af.showProductsFor(af.viewport.getSequenceSelection(), ds,
3745                     isRegSel, dna, source);
3746           }
3747
3748         });
3749         showProducts.add(xtype);
3750       }
3751       showProducts.setVisible(showp);
3752       showProducts.setEnabled(showp);
3753     } catch (Exception e)
3754     {
3755       jalview.bin.Cache.log
3756               .warn(
3757                       "canTranslate threw an exception - please report to help@jalview.org",
3758                       e);
3759       return false;
3760     }
3761     return showp;
3762   }
3763
3764   protected void showProductsFor(SequenceI[] sel, Alignment ds,
3765           boolean isRegSel, boolean dna, String source)
3766   {
3767     final boolean fisRegSel = isRegSel;
3768     final boolean fdna = dna;
3769     final String fsrc = source;
3770     final AlignFrame ths = this;
3771     final SequenceI[] fsel = sel;
3772     Runnable foo = new Runnable()
3773     {
3774
3775       public void run()
3776       {
3777         final long sttime = System.currentTimeMillis();
3778         ths.setProgressBar("Searching for sequences from " + fsrc, sttime);
3779         try
3780         {
3781           Alignment ds = ths.getViewport().alignment.getDataset(); // update
3782           // our local
3783           // dataset
3784           // reference
3785           Alignment prods = CrossRef
3786                   .findXrefSequences(fsel, fdna, fsrc, ds);
3787           if (prods != null)
3788           {
3789             SequenceI[] sprods = new SequenceI[prods.getHeight()];
3790             for (int s = 0; s < sprods.length; s++)
3791             {
3792               sprods[s] = (prods.getSequenceAt(s)).deriveSequence();
3793               if (ds.getSequences() == null
3794                       || !ds.getSequences().contains(
3795                               sprods[s].getDatasetSequence()))
3796                 ds.addSequence(sprods[s].getDatasetSequence());
3797               sprods[s].updatePDBIds();
3798             }
3799             Alignment al = new Alignment(sprods);
3800             AlignedCodonFrame[] cf = prods.getCodonFrames();
3801             al.setDataset(ds);
3802             for (int s = 0; cf != null && s < cf.length; s++)
3803             {
3804               al.addCodonFrame(cf[s]);
3805               cf[s] = null;
3806             }
3807             AlignFrame naf = new AlignFrame(al, DEFAULT_WIDTH,
3808                     DEFAULT_HEIGHT);
3809             String newtitle = "" + ((fdna) ? "Proteins " : "Nucleotides ")
3810                     + " for " + ((fisRegSel) ? "selected region of " : "")
3811                     + getTitle();
3812             Desktop.addInternalFrame(naf, newtitle, DEFAULT_WIDTH,
3813                     DEFAULT_HEIGHT);
3814           }
3815           else
3816           {
3817             System.err.println("No Sequences generated for xRef type "
3818                     + fsrc);
3819           }
3820         } catch (Exception e)
3821         {
3822           jalview.bin.Cache.log.error(
3823                   "Exception when finding crossreferences", e);
3824         } catch (OutOfMemoryError e)
3825         {
3826           new OOMWarning("whilst fetching crossreferences", e);
3827         } catch (Error e)
3828         {
3829           jalview.bin.Cache.log.error("Error when finding crossreferences",
3830                   e);
3831         }
3832         ths.setProgressBar("Finished searching for sequences from " + fsrc,
3833                 sttime);
3834       }
3835
3836     };
3837     Thread frunner = new Thread(foo);
3838     frunner.start();
3839   }
3840
3841   public boolean canShowTranslationProducts(SequenceI[] selection,
3842           AlignmentI alignment)
3843   {
3844     // old way
3845     try
3846     {
3847       return (jalview.analysis.Dna.canTranslate(selection, viewport
3848               .getViewAsVisibleContigs(true)));
3849     } catch (Exception e)
3850     {
3851       jalview.bin.Cache.log
3852               .warn(
3853                       "canTranslate threw an exception - please report to help@jalview.org",
3854                       e);
3855       return false;
3856     }
3857   }
3858
3859   public void showProducts_actionPerformed(ActionEvent e)
3860   {
3861     // /////////////////////////////
3862     // Collect Data to be translated/transferred
3863
3864     SequenceI[] selection = viewport.getSequenceSelection();
3865     AlignmentI al = null;
3866     try
3867     {
3868       al = jalview.analysis.Dna.CdnaTranslate(selection, viewport
3869               .getViewAsVisibleContigs(true), viewport.getGapCharacter(),
3870               viewport.getAlignment().getDataset());
3871     } catch (Exception ex)
3872     {
3873       al = null;
3874       jalview.bin.Cache.log.debug("Exception during translation.", ex);
3875     }
3876     if (al == null)
3877     {
3878       JOptionPane
3879               .showMessageDialog(
3880                       Desktop.desktop,
3881                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
3882                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
3883     }
3884     else
3885     {
3886       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
3887       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
3888               DEFAULT_WIDTH, DEFAULT_HEIGHT);
3889     }
3890   }
3891
3892   public void showTranslation_actionPerformed(ActionEvent e)
3893   {
3894     // /////////////////////////////
3895     // Collect Data to be translated/transferred
3896
3897     SequenceI[] selection = viewport.getSequenceSelection();
3898     String[] seqstring = viewport.getViewAsString(true);
3899     AlignmentI al = null;
3900     try
3901     {
3902       al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring,
3903               viewport.getViewAsVisibleContigs(true), viewport
3904                       .getGapCharacter(), viewport.alignment
3905                       .getAlignmentAnnotation(), viewport.alignment
3906                       .getWidth(), viewport.getAlignment().getDataset());
3907     } catch (Exception ex)
3908     {
3909       al = null;
3910       jalview.bin.Cache.log.debug("Exception during translation.", ex);
3911     }
3912     if (al == null)
3913     {
3914       JOptionPane
3915               .showMessageDialog(
3916                       Desktop.desktop,
3917                       "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
3918                       "Translation Failed", JOptionPane.WARNING_MESSAGE);
3919     }
3920     else
3921     {
3922       AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
3923       Desktop.addInternalFrame(af, "Translation of " + this.getTitle(),
3924               DEFAULT_WIDTH, DEFAULT_HEIGHT);
3925     }
3926   }
3927
3928   /**
3929    * Try to load a features file onto the alignment.
3930    * 
3931    * @param file
3932    *                contents or path to retrieve file
3933    * @param type
3934    *                access mode of file (see jalview.io.AlignFile)
3935    * @return true if features file was parsed corectly.
3936    */
3937   public boolean parseFeaturesFile(String file, String type)
3938   {
3939     boolean featuresFile = false;
3940     try
3941     {
3942       featuresFile = new FeaturesFile(file, type).parse(viewport.alignment
3943               .getDataset(), alignPanel.seqPanel.seqCanvas
3944               .getFeatureRenderer().featureColours, false);
3945     } catch (Exception ex)
3946     {
3947       ex.printStackTrace();
3948     }
3949
3950     if (featuresFile)
3951     {
3952       viewport.showSequenceFeatures = true;
3953       showSeqFeatures.setSelected(true);
3954       alignPanel.paintAlignment(true);
3955     }
3956
3957     return featuresFile;
3958   }
3959
3960   public void dragEnter(DropTargetDragEvent evt)
3961   {
3962   }
3963
3964   public void dragExit(DropTargetEvent evt)
3965   {
3966   }
3967
3968   public void dragOver(DropTargetDragEvent evt)
3969   {
3970   }
3971
3972   public void dropActionChanged(DropTargetDragEvent evt)
3973   {
3974   }
3975
3976   public void drop(DropTargetDropEvent evt)
3977   {
3978     Transferable t = evt.getTransferable();
3979     java.util.List files = null;
3980
3981     try
3982     {
3983       DataFlavor uriListFlavor = new DataFlavor(
3984               "text/uri-list;class=java.lang.String");
3985       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3986       {
3987         // Works on Windows and MacOSX
3988         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3989         files = (java.util.List) t
3990                 .getTransferData(DataFlavor.javaFileListFlavor);
3991       }
3992       else if (t.isDataFlavorSupported(uriListFlavor))
3993       {
3994         // This is used by Unix drag system
3995         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3996         String data = (String) t.getTransferData(uriListFlavor);
3997         files = new java.util.ArrayList(1);
3998         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3999                 data, "\r\n"); st.hasMoreTokens();)
4000         {
4001           String s = st.nextToken();
4002           if (s.startsWith("#"))
4003           {
4004             // the line is a comment (as per the RFC 2483)
4005             continue;
4006           }
4007
4008           java.net.URI uri = new java.net.URI(s);
4009           java.io.File file = new java.io.File(uri);
4010           files.add(file);
4011         }
4012       }
4013     } catch (Exception e)
4014     {
4015       e.printStackTrace();
4016     }
4017     if (files != null)
4018     {
4019       try
4020       {
4021
4022         for (int i = 0; i < files.size(); i++)
4023         {
4024           loadJalviewDataFile(files.get(i).toString());
4025         }
4026       } catch (Exception ex)
4027       {
4028         ex.printStackTrace();
4029       }
4030     }
4031   }
4032
4033   /**
4034    * Attempt to load a "dropped" file: First by testing whether it's and
4035    * Annotation file, then a JNet file, and finally a features file. If all are
4036    * false then the user may have dropped an alignment file onto this
4037    * AlignFrame.
4038    * 
4039    * @param file
4040    *                either a filename or a URL string.
4041    */
4042   public void loadJalviewDataFile(String file)
4043   {
4044     try
4045     {
4046       String protocol = "File";
4047
4048       if (file.indexOf("http:") > -1 || file.indexOf("file:") > -1)
4049       {
4050         protocol = "URL";
4051       }
4052
4053       boolean isAnnotation = new AnnotationFile().readAnnotationFile(
4054               viewport.alignment, file, protocol);
4055
4056       if (!isAnnotation)
4057       {
4058         // try to see if its a JNet 'concise' style annotation file *before* we
4059         // try to parse it as a features file
4060         String format = new IdentifyFile().Identify(file, protocol);
4061         if (format.equalsIgnoreCase("JnetFile"))
4062         {
4063           jalview.io.JPredFile predictions = new jalview.io.JPredFile(file,
4064                   protocol);
4065           new JnetAnnotationMaker().add_annotation(predictions, viewport
4066                   .getAlignment(), 0, false);
4067           isAnnotation = true;
4068         }
4069         else
4070         {
4071           // try to parse it as a features file
4072           boolean isGroupsFile = parseFeaturesFile(file, protocol);
4073           // if it wasn't a features file then we just treat it as a general
4074           // alignment file to load into the current view.
4075           if (!isGroupsFile)
4076           {
4077             new FileLoader().LoadFile(viewport, file, protocol, format);
4078           }
4079           else
4080           {
4081             alignPanel.paintAlignment(true);
4082           }
4083         }
4084       }
4085       if (isAnnotation)
4086       {
4087
4088         alignPanel.adjustAnnotationHeight();
4089         viewport.updateSequenceIdColours();
4090         buildSortByAnnotationScoresMenu();
4091         alignPanel.paintAlignment(true);
4092       }
4093     } catch (Exception ex)
4094     {
4095       ex.printStackTrace();
4096     }
4097   }
4098
4099   public void tabSelectionChanged(int index)
4100   {
4101     if (index > -1)
4102     {
4103       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
4104       viewport = alignPanel.av;
4105       setMenusFromViewport(viewport);
4106     }
4107   }
4108
4109   public void tabbedPane_mousePressed(MouseEvent e)
4110   {
4111     if (SwingUtilities.isRightMouseButton(e))
4112     {
4113       String reply = JOptionPane.showInternalInputDialog(this,
4114               "Enter View Name", "Edit View Name",
4115               JOptionPane.QUESTION_MESSAGE);
4116
4117       if (reply != null)
4118       {
4119         viewport.viewName = reply;
4120         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
4121       }
4122     }
4123   }
4124
4125   public AlignViewport getCurrentView()
4126   {
4127     return viewport;
4128   }
4129
4130   /**
4131    * Open the dialog for regex description parsing.
4132    */
4133   protected void extractScores_actionPerformed(ActionEvent e)
4134   {
4135     ParseProperties pp = new jalview.analysis.ParseProperties(
4136             viewport.alignment);
4137     // TODO: verify regex and introduce GUI dialog for version 2.5
4138     // if (pp.getScoresFromDescription("col", "score column ",
4139     // "\\W*([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)",
4140     // true)>0)
4141     if (pp.getScoresFromDescription("description column",
4142             "score in description column ", "\\W*([-+eE0-9.]+)", true) > 0)
4143     {
4144       buildSortByAnnotationScoresMenu();
4145     }
4146   }
4147
4148   /* (non-Javadoc)
4149    * @see jalview.jbgui.GAlignFrame#showDbRefs_actionPerformed(java.awt.event.ActionEvent)
4150    */
4151   protected void showDbRefs_actionPerformed(ActionEvent e)
4152   {
4153     viewport.setShowDbRefs(showDbRefsMenuitem.isSelected());
4154   }
4155
4156   /* (non-Javadoc)
4157    * @see jalview.jbgui.GAlignFrame#showNpFeats_actionPerformed(java.awt.event.ActionEvent)
4158    */
4159   protected void showNpFeats_actionPerformed(ActionEvent e)
4160   {
4161     viewport.setShowNpFeats(showNpFeatsMenuitem.isSelected());
4162   }
4163
4164   /**
4165    * find the viewport amongst the tabs in this alignment frame and close that tab
4166    * @param av
4167    */
4168   public boolean closeView(AlignViewport av)
4169   {
4170     if (viewport == av)
4171     {
4172       this.closeMenuItem_actionPerformed(false);
4173       return true;
4174     }
4175     Component[] comp = tabbedPane.getComponents();
4176     for (int i=0;comp!=null && i<comp.length;i++)
4177     {
4178       if (comp[i] instanceof AlignmentPanel)
4179       {
4180         if (((AlignmentPanel) comp[i]).av == av)
4181         {
4182           // close the view.
4183           closeView((AlignmentPanel) comp[i]);
4184           return true;
4185         }
4186       }
4187     }
4188     return false;
4189   }
4190   protected void build_fetchdbmenu(JMenu webService) {
4191     // Temporary hack - DBRef Fetcher always top level ws entry.
4192     // TODO We probably want to store a sequence database checklist in preferences and have checkboxes.. rather than individual sources selected here
4193     JMenu rfetch = new JMenu("Fetch DB References");
4194     rfetch
4195             .setToolTipText("Retrieve and parse sequence database records for the alignment or the currently selected sequences");
4196     webService.add(rfetch);
4197
4198     JMenuItem fetchr = new JMenuItem("Standard Databases");
4199     fetchr.setToolTipText("Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources");
4200     fetchr.addActionListener(new ActionListener()
4201     {
4202
4203       public void actionPerformed(ActionEvent e)
4204       {
4205         new Thread(new Runnable()
4206         {
4207
4208           public void run()
4209           {
4210             new jalview.ws.DBRefFetcher(alignPanel.av
4211                     .getSequenceSelection(), alignPanel.alignFrame)
4212                     .fetchDBRefs(false);
4213           }
4214         }).start();
4215
4216       }
4217
4218     });
4219     rfetch.add(fetchr);
4220     JMenu dfetch = new JMenu();
4221     rfetch.add(dfetch);
4222     jalview.ws.SequenceFetcher sf = SequenceFetcher.getSequenceFetcherSingleton(this);
4223     String[] otherdb = sf.getOrderedSupportedSources(); 
4224     // sf.getDbInstances(jalview.ws.dbsources.DasSequenceSource.class);
4225     // jalview.util.QuickSort.sort(otherdb, otherdb);
4226     int comp=0,mcomp=15;
4227     String mname=null;
4228     if (otherdb!=null && otherdb.length>0)
4229     {
4230       for (int i=0; i<otherdb.length; i++)
4231       {
4232         String dbname =sf.getSourceProxy(otherdb[i]).getDbName();
4233         if (mname == null)
4234         {
4235           mname = "from '"+dbname+"'";
4236         }
4237         fetchr = new JMenuItem(otherdb[i]);
4238         final String[] dassource = new String[] { otherdb[i] };
4239         fetchr.addActionListener(new ActionListener()
4240         {
4241
4242           public void actionPerformed(ActionEvent e)
4243           {
4244             new Thread(new Runnable()
4245             {
4246
4247               public void run()
4248               {
4249                 new jalview.ws.DBRefFetcher(alignPanel.av
4250                         .getSequenceSelection(), alignPanel.alignFrame, dassource)
4251                         .fetchDBRefs(false);
4252               }
4253             }).start();
4254           }
4255           
4256         });
4257         fetchr.setToolTipText("Retrieve from "+dbname);
4258         dfetch.add(fetchr);
4259         if (comp++==mcomp || i==(otherdb.length-1))
4260         {
4261           dfetch.setText(mname+" to '"+dbname+"'");
4262           rfetch.add(dfetch);
4263           dfetch = new JMenu();
4264           mname = null;
4265           comp=0;
4266         }
4267       }
4268     }
4269   }
4270 }
4271
4272 class PrintThread extends Thread
4273 {
4274   AlignmentPanel ap;
4275
4276   public PrintThread(AlignmentPanel ap)
4277   {
4278     this.ap = ap;
4279   }
4280
4281   static PageFormat pf;
4282
4283   public void run()
4284   {
4285     PrinterJob printJob = PrinterJob.getPrinterJob();
4286
4287     if (pf != null)
4288     {
4289       printJob.setPrintable(ap, pf);
4290     }
4291     else
4292     {
4293       printJob.setPrintable(ap);
4294     }
4295
4296     if (printJob.printDialog())
4297     {
4298       try
4299       {
4300         printJob.print();
4301       } catch (Exception PrintException)
4302       {
4303         PrintException.printStackTrace();
4304       }
4305     }
4306   }
4307 }