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