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