c7887da9c92e55cab3fccba11f0a9e3f8799c9e4
[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    * @return alignment objects for all views
1055    */
1056   AlignmentI[] getViewAlignments()
1057   {
1058     if (alignPanels!=null)
1059     {
1060       Enumeration e = alignPanels.elements();
1061       AlignmentI[] als = new AlignmentI[alignPanels.size()];
1062       for (int i=0; e.hasMoreElements(); i++)
1063       {
1064         als[i] = ((AlignmentPanel) e.nextElement()).av.getAlignment();
1065       }
1066       return als;
1067     }
1068     if (viewport!=null)
1069     {
1070       return new AlignmentI[] { viewport.alignment };
1071     }
1072     return null;
1073   }
1074   /**
1075    * DOCUMENT ME!
1076    *
1077    * @param e DOCUMENT ME!
1078    */
1079   protected void undoMenuItem_actionPerformed(ActionEvent e)
1080   {
1081     if (viewport.historyList.empty())
1082       return;
1083     CommandI command = (CommandI)viewport.historyList.pop();
1084     viewport.redoList.push(command);
1085     command.undoCommand(getViewAlignments());
1086
1087     AlignViewport originalSource = getOriginatingSource(command);
1088     updateEditMenuBar();
1089
1090     if(originalSource!=null)
1091     {
1092       originalSource.hasHiddenColumns = viewport.colSel.getHiddenColumns() != null;
1093       originalSource.firePropertyChange("alignment",
1094                                         null,
1095                                         originalSource.alignment.getSequences());
1096     }
1097   }
1098
1099   /**
1100    * DOCUMENT ME!
1101    *
1102    * @param e DOCUMENT ME!
1103    */
1104   protected void redoMenuItem_actionPerformed(ActionEvent e)
1105   {
1106     if(viewport.redoList.size()<1)
1107     {
1108       return;
1109     }
1110
1111     CommandI command = (CommandI) viewport.redoList.pop();
1112     viewport.historyList.push(command);
1113     command.doCommand(getViewAlignments());
1114
1115     AlignViewport originalSource = getOriginatingSource(command);
1116     updateEditMenuBar();
1117
1118     if(originalSource!=null)
1119     {
1120       originalSource.hasHiddenColumns = viewport.colSel.getHiddenColumns() != null;
1121       originalSource.firePropertyChange("alignment",
1122                                         null,
1123                                         originalSource.alignment.getSequences());
1124     }
1125   }
1126
1127   AlignViewport getOriginatingSource(CommandI command)
1128   {
1129     AlignViewport originalSource = null;
1130     //For sequence removal and addition, we need to fire
1131    //the property change event FROM the viewport where the
1132    //original alignment was altered
1133     AlignmentI al=null;
1134     if (command instanceof EditCommand)
1135     {
1136       EditCommand editCommand = (EditCommand) command;
1137       al = editCommand.getAlignment();
1138       Vector comps = (Vector) PaintRefresher.components
1139           .get(viewport.getSequenceSetId());
1140
1141       for (int i = 0; i < comps.size(); i++)
1142       {
1143         if (comps.elementAt(i) instanceof AlignmentPanel)
1144         {
1145           if (al == ( (AlignmentPanel) comps.elementAt(i)).av.alignment)
1146           {
1147             originalSource = ( (AlignmentPanel) comps.elementAt(i)).av;
1148             break;
1149           }
1150         }
1151       }
1152     }
1153
1154     if (originalSource == null)
1155     {
1156       //The original view is closed, we must validate
1157       //the current view against the closed view first
1158       if (al != null)
1159       {
1160         PaintRefresher.validateSequences(al, viewport.alignment);
1161       }
1162
1163       originalSource = viewport;
1164     }
1165
1166     return originalSource;
1167   }
1168
1169   /**
1170    * DOCUMENT ME!
1171    *
1172    * @param up DOCUMENT ME!
1173    */
1174   public void moveSelectedSequences(boolean up)
1175   {
1176     SequenceGroup sg = viewport.getSelectionGroup();
1177
1178     if (sg == null)
1179     {
1180       return;
1181     }
1182
1183     if (up)
1184     {
1185       for (int i = 1; i < viewport.alignment.getHeight(); 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     else
1206     {
1207       for (int i = viewport.alignment.getHeight() - 2; i > -1; i--)
1208       {
1209         SequenceI seq = viewport.alignment.getSequenceAt(i);
1210
1211         if (!sg.getSequences(null).contains(seq))
1212         {
1213           continue;
1214         }
1215
1216         SequenceI temp = viewport.alignment.getSequenceAt(i + 1);
1217
1218         if (sg.getSequences(null).contains(temp))
1219         {
1220           continue;
1221         }
1222
1223         viewport.alignment.getSequences().setElementAt(temp, i);
1224         viewport.alignment.getSequences().setElementAt(seq, i + 1);
1225       }
1226     }
1227
1228     alignPanel.paintAlignment(true);
1229   }
1230
1231
1232
1233
1234   synchronized void slideSequences(boolean right, int size)
1235   {
1236     Vector sg = new Vector();
1237     if(viewport.cursorMode)
1238     {
1239       sg.addElement(viewport.alignment.getSequenceAt(
1240           alignPanel.seqPanel.seqCanvas.cursorY));
1241     }
1242     else if(viewport.getSelectionGroup()!=null
1243         && viewport.getSelectionGroup().getSize()!=viewport.alignment.getHeight())
1244    {
1245      sg = viewport.getSelectionGroup().getSequences(
1246          viewport.hiddenRepSequences);
1247    }
1248
1249     if(sg.size()<1)
1250     {
1251       return;
1252     }
1253
1254     Vector invertGroup = new Vector();
1255
1256     for (int i = 0; i < viewport.alignment.getHeight(); i++)
1257     {
1258       if(!sg.contains(viewport.alignment.getSequenceAt(i)))
1259          invertGroup.add(viewport.alignment.getSequenceAt(i));
1260     }
1261
1262     SequenceI[] seqs1 = new SequenceI[sg.size()];
1263     for (int i = 0; i < sg.size(); i++)
1264       seqs1[i] = (SequenceI) sg.elementAt(i);
1265
1266     SequenceI[] seqs2 = new SequenceI[invertGroup.size()];
1267     for (int i = 0; i < invertGroup.size(); i++)
1268       seqs2[i] = (SequenceI) invertGroup.elementAt(i);
1269
1270     SlideSequencesCommand ssc;
1271     if (right)
1272       ssc = new SlideSequencesCommand("Slide Sequences",
1273                                       seqs2, seqs1, size,
1274                                       viewport.getGapCharacter()
1275           );
1276     else
1277       ssc = new SlideSequencesCommand("Slide Sequences",
1278                                       seqs1, seqs2, size,
1279                                       viewport.getGapCharacter()
1280           );
1281
1282     int groupAdjustment = 0;
1283     if (ssc.getGapsInsertedBegin() && right)
1284     {
1285       if (viewport.cursorMode)
1286         alignPanel.seqPanel.moveCursor(size, 0);
1287       else
1288         groupAdjustment = size;
1289     }
1290     else if (!ssc.getGapsInsertedBegin() && !right)
1291     {
1292       if (viewport.cursorMode)
1293         alignPanel.seqPanel.moveCursor( -size, 0);
1294       else
1295         groupAdjustment = -size;
1296     }
1297
1298     if (groupAdjustment != 0)
1299     {
1300       viewport.getSelectionGroup().setStartRes(
1301           viewport.getSelectionGroup().getStartRes() + groupAdjustment);
1302       viewport.getSelectionGroup().setEndRes(
1303           viewport.getSelectionGroup().getEndRes() + groupAdjustment);
1304     }
1305
1306
1307     boolean appendHistoryItem = false;
1308     if(viewport.historyList!=null
1309        && viewport.historyList.size()>0
1310       && viewport.historyList.peek() instanceof SlideSequencesCommand)
1311     {
1312       appendHistoryItem = ssc.appendSlideCommand(
1313           (SlideSequencesCommand)viewport.historyList.peek())
1314           ;
1315     }
1316
1317     if(!appendHistoryItem)
1318       addHistoryItem(ssc);
1319
1320     repaint();
1321   }
1322
1323
1324   /**
1325    * DOCUMENT ME!
1326    *
1327    * @param e DOCUMENT ME!
1328    */
1329   protected void copy_actionPerformed(ActionEvent e)
1330   {
1331     System.gc();
1332     if (viewport.getSelectionGroup() == null)
1333     {
1334       return;
1335     }
1336     //  TODO: preserve the ordering of displayed alignment annotation in any internal paste (particularly sequence associated annotation)
1337     SequenceI [] seqs = viewport.getSelectionAsNewSequence();
1338     String[] omitHidden = null;
1339
1340     if (viewport.hasHiddenColumns)
1341     {
1342       omitHidden = viewport.getViewAsString(true);
1343     }
1344
1345     String output = new FormatAdapter().formatSequences(
1346         "Fasta",
1347         seqs,
1348         omitHidden);
1349
1350     StringSelection ss = new StringSelection(output);
1351
1352     try
1353     {
1354       jalview.gui.Desktop.internalCopy = true;
1355       //Its really worth setting the clipboard contents
1356       //to empty before setting the large StringSelection!!
1357       Toolkit.getDefaultToolkit().getSystemClipboard()
1358           .setContents(new StringSelection(""), null);
1359
1360       Toolkit.getDefaultToolkit().getSystemClipboard()
1361           .setContents(ss, Desktop.instance);
1362     }
1363     catch (OutOfMemoryError er)
1364     {
1365       er.printStackTrace();
1366       javax.swing.SwingUtilities.invokeLater(new Runnable()
1367           {
1368             public void run()
1369             {
1370               javax.swing.JOptionPane.showInternalMessageDialog(Desktop.desktop,
1371                   "Out of memory copying region!!"
1372                   +
1373                   "\nSee help files for increasing Java Virtual Machine memory."
1374                   , "Out of memory",
1375                   javax.swing.JOptionPane.WARNING_MESSAGE);
1376             }
1377           });
1378
1379       return;
1380     }
1381
1382     Vector hiddenColumns = null;
1383     if(viewport.hasHiddenColumns)
1384     {
1385       hiddenColumns =new Vector();
1386       int hiddenOffset = viewport.getSelectionGroup().getStartRes();
1387       for (int i = 0; i < viewport.getColumnSelection().getHiddenColumns().size();
1388            i++)
1389       {
1390         int[] region = (int[])
1391             viewport.getColumnSelection().getHiddenColumns().elementAt(i);
1392
1393         hiddenColumns.addElement(new int[]
1394                                  {region[0] - hiddenOffset,
1395                           region[1]-hiddenOffset});
1396       }
1397     }
1398
1399     Desktop.jalviewClipboard = new Object[]
1400         {
1401         seqs,
1402         viewport.alignment.getDataset(),
1403         hiddenColumns};
1404     statusBar.setText("Copied "+seqs.length+" sequences to clipboard.");
1405   }
1406
1407   /**
1408    * DOCUMENT ME!
1409    *
1410    * @param e DOCUMENT ME!
1411    */
1412   protected void pasteNew_actionPerformed(ActionEvent e)
1413   {
1414     paste(true);
1415   }
1416
1417   /**
1418    * DOCUMENT ME!
1419    *
1420    * @param e DOCUMENT ME!
1421    */
1422   protected void pasteThis_actionPerformed(ActionEvent e)
1423   {
1424     paste(false);
1425   }
1426
1427   /**
1428    * DOCUMENT ME!
1429    *
1430    * @param newAlignment DOCUMENT ME!
1431    */
1432   void paste(boolean newAlignment)
1433   {
1434     boolean externalPaste=true;
1435     try
1436     {
1437       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
1438       Transferable contents = c.getContents(this);
1439
1440       if (contents == null)
1441       {
1442         return;
1443       }
1444
1445       String str, format;
1446       try
1447       {
1448         str = (String) contents.getTransferData(DataFlavor.stringFlavor);
1449         if (str.length() < 1)
1450         {
1451           return;
1452         }
1453
1454         format = new IdentifyFile().Identify(str, "Paste");
1455
1456       }
1457       catch (OutOfMemoryError er)
1458       {
1459         er.printStackTrace();
1460         javax.swing.SwingUtilities.invokeLater(new Runnable()
1461         {
1462           public void run()
1463           {
1464             javax.swing.JOptionPane.showInternalMessageDialog(Desktop.desktop,
1465                 "Out of memory pasting sequences!!"
1466                 +
1467                 "\nSee help files for increasing Java Virtual Machine memory."
1468                 , "Out of memory",
1469                 javax.swing.JOptionPane.WARNING_MESSAGE);
1470           }
1471         });
1472
1473         return;
1474       }
1475
1476       SequenceI[] sequences;
1477       boolean annotationAdded = false;
1478       AlignmentI alignment = null;
1479
1480      if(Desktop.jalviewClipboard!=null)
1481      {
1482        // The clipboard was filled from within Jalview, we must use the sequences
1483        // And dataset from the copied alignment
1484        SequenceI[] newseq = (SequenceI[])Desktop.jalviewClipboard[0];
1485        // be doubly sure that we create *new* sequence objects.
1486        sequences = new SequenceI[newseq.length];
1487        for (int i=0;i<newseq.length;i++) {
1488          sequences[i] = new Sequence(newseq[i]);
1489        }
1490        alignment = new Alignment(sequences);
1491        externalPaste = false;
1492      }
1493      else
1494      {
1495        // parse the clipboard as an alignment.
1496        alignment = new FormatAdapter().readFile(str, "Paste", format);
1497        sequences = alignment.getSequencesArray();
1498      }
1499
1500      int alwidth=0;
1501
1502      if (newAlignment)
1503      {
1504
1505        if (Desktop.jalviewClipboard != null)
1506        {
1507          // dataset is inherited
1508          alignment.setDataset( (Alignment) Desktop.jalviewClipboard[1]);
1509        }
1510        else
1511        {
1512          // new dataset is constructed
1513          alignment.setDataset(null);
1514        }
1515        alwidth = alignment.getWidth()+1;
1516      }
1517      else
1518      {
1519        AlignmentI pastedal = alignment; // preserve pasted alignment object
1520        // Add pasted sequences and dataset into existing alignment.
1521        alignment = viewport.getAlignment();
1522        alwidth = alignment.getWidth()+1;
1523         // decide if we need to import sequences from an existing dataset
1524         boolean importDs = Desktop.jalviewClipboard != null
1525                 && Desktop.jalviewClipboard[1] != alignment.getDataset();
1526         // importDs==true instructs us to copy over new dataset sequences from
1527         // an existing alignment
1528         Vector newDs = (importDs) ? new Vector() : null; // used to create
1529                                                           // minimum dataset set
1530
1531         for (int i = 0; i < sequences.length; i++)
1532         {
1533           if (importDs)
1534           {
1535             newDs.addElement(null);
1536           }
1537           SequenceI ds = sequences[i].getDatasetSequence(); // null for a simple
1538                                                             // paste
1539           if (importDs && ds != null)
1540           {
1541             if (!newDs.contains(ds))
1542             {
1543               newDs.setElementAt(ds, i);
1544               ds = new Sequence(ds);
1545               // update with new dataset sequence
1546               sequences[i].setDatasetSequence(ds);
1547             }
1548             else
1549             {
1550               ds = sequences[newDs.indexOf(ds)].getDatasetSequence();
1551             }
1552           }
1553           else
1554           {
1555             // copy and derive new dataset sequence
1556             sequences[i] = sequences[i].deriveSequence();
1557             alignment.getDataset().addSequence(sequences[i].getDatasetSequence());
1558             // TODO: avoid creation of duplicate dataset sequences with a
1559             // 'contains' method using SequenceI.equals()/SequenceI.contains()
1560           }
1561           alignment.addSequence(sequences[i]); // merges dataset
1562         }
1563         if (newDs != null)
1564         {
1565           newDs.clear(); // tidy up
1566         }
1567         if (pastedal.getAlignmentAnnotation()!=null) {
1568           // Add any annotation attached to alignment.
1569           AlignmentAnnotation[] alann = pastedal.getAlignmentAnnotation();
1570           for (int i=0; i<alann.length; i++)
1571           {
1572             annotationAdded=true;
1573             if (alann[i].sequenceRef==null && !alann[i].autoCalculated) {
1574               AlignmentAnnotation newann = new AlignmentAnnotation(alann[i]);
1575               newann.padAnnotation(alwidth);
1576               alignment.addAnnotation(newann);
1577             }
1578           }
1579         }
1580      }
1581      if (!newAlignment) {
1582        ///////
1583        // ADD HISTORY ITEM
1584        //
1585        addHistoryItem(new EditCommand(
1586                "Add sequences",
1587                EditCommand.PASTE,
1588                sequences,
1589                0,
1590                alignment.getWidth(),
1591                alignment)
1592               );
1593      }
1594      // Add any annotations attached to sequences
1595      for (int i = 0; i < sequences.length; i++)
1596      {
1597        if (sequences[i].getAnnotation() != null)
1598        {
1599          for (int a = 0; a < sequences[i].getAnnotation().length; a++)
1600          {
1601            annotationAdded=true;
1602            sequences[i].getAnnotation()[a].adjustForAlignment();
1603            sequences[i].getAnnotation()[a].padAnnotation(alwidth);
1604            alignment.addAnnotation(sequences[i].getAnnotation()[a]); // annotation was duplicated earlier
1605            alignment.setAnnotationIndex(sequences[i].getAnnotation()[a], a);
1606          }
1607        }
1608      }
1609      if (!newAlignment) {
1610
1611        // propagate alignment changed.
1612        viewport.setEndSeq(alignment.getHeight());
1613        if (annotationAdded)
1614        {
1615          // Duplicate sequence annotation in all views.
1616          AlignmentI[] alview = this.getViewAlignments();
1617          for (int i = 0; i < sequences.length; i++)
1618          {
1619            AlignmentAnnotation sann[] = sequences[i].getAnnotation();
1620            if (sann == null)
1621              continue;
1622            for (int avnum=0;avnum<alview.length; avnum++)
1623            {
1624              if (alview[avnum]!=alignment)
1625              {
1626                // duplicate in a view other than the one with input focus
1627                int avwidth = alview[avnum].getWidth()+1;
1628                // this relies on sann being preserved after we 
1629                // modify the sequence's annotation array for each duplication
1630                for (int a=0; a<sann.length; a++)
1631                {
1632                  AlignmentAnnotation newann = new AlignmentAnnotation(sann[a]);
1633                  sequences[i].addAlignmentAnnotation(newann);
1634                  newann.padAnnotation(avwidth);
1635                  alview[avnum].addAnnotation(newann); // annotation was duplicated earlier
1636                  alview[avnum].setAnnotationIndex(newann, a);
1637                }
1638              }
1639            }
1640          }
1641          buildSortByAnnotationScoresMenu();
1642        }
1643        viewport.firePropertyChange("alignment", null, alignment.getSequences());
1644
1645      } else {
1646        AlignFrame af = new AlignFrame(alignment, DEFAULT_WIDTH, DEFAULT_HEIGHT);
1647        String newtitle = new String("Copied sequences");
1648
1649        if(Desktop.jalviewClipboard!=null && Desktop.jalviewClipboard[2]!=null)
1650          {
1651            Vector hc = (Vector)Desktop.jalviewClipboard[2];
1652            for(int i=0; i<hc.size(); i++)
1653            {
1654              int [] region = (int[]) hc.elementAt(i);
1655              af.viewport.hideColumns(region[0], region[1]);
1656            }
1657          }
1658
1659
1660        //>>>This is a fix for the moment, until a better solution is found!!<<<
1661        af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().transferSettings(
1662            alignPanel.seqPanel.seqCanvas.getFeatureRenderer());
1663
1664        // TODO: maintain provenance of an alignment, rather than just make the title a concatenation of operations.
1665        if (!externalPaste) {
1666          if (title.startsWith("Copied sequences"))
1667          {
1668            newtitle = title;
1669          }
1670          else
1671          {
1672            newtitle = newtitle.concat("- from " + title);
1673          }
1674        } else {
1675          newtitle = new String("Pasted sequences");
1676        }
1677
1678        Desktop.addInternalFrame(af, newtitle, DEFAULT_WIDTH,
1679                                 DEFAULT_HEIGHT);
1680
1681      }
1682
1683
1684     }
1685     catch (Exception ex)
1686     {
1687       ex.printStackTrace();
1688         System.out.println("Exception whilst pasting: "+ex);
1689         // could be anything being pasted in here
1690     }
1691
1692
1693   }
1694
1695   /**
1696    * DOCUMENT ME!
1697    *
1698    * @param e DOCUMENT ME!
1699    */
1700   protected void cut_actionPerformed(ActionEvent e)
1701   {
1702     copy_actionPerformed(null);
1703     delete_actionPerformed(null);
1704   }
1705
1706   /**
1707    * DOCUMENT ME!
1708    *
1709    * @param e DOCUMENT ME!
1710    */
1711   protected void delete_actionPerformed(ActionEvent evt)
1712   {
1713
1714     SequenceGroup sg = viewport.getSelectionGroup();
1715     if (sg == null)
1716     {
1717       return;
1718     }
1719
1720     Vector seqs = new Vector();
1721     SequenceI seq;
1722     for (int i = 0; i < sg.getSize(); i++)
1723     {
1724       seq = sg.getSequenceAt(i);
1725       seqs.addElement(seq);
1726     }
1727
1728
1729    // If the cut affects all sequences, remove highlighted columns
1730    if (sg.getSize() == viewport.alignment.getHeight())
1731    {
1732      viewport.getColumnSelection().removeElements(sg.getStartRes(),
1733          sg.getEndRes() + 1);
1734    }
1735
1736
1737     SequenceI [] cut = new SequenceI[seqs.size()];
1738     for(int i=0; i<seqs.size(); i++)
1739     {
1740       cut[i] = (SequenceI)seqs.elementAt(i);
1741     }
1742
1743
1744     /*
1745     //ADD HISTORY ITEM
1746     */
1747     addHistoryItem(new EditCommand("Cut Sequences",
1748                                       EditCommand.CUT,
1749                                       cut,
1750                                       sg.getStartRes(),
1751                                       sg.getEndRes()-sg.getStartRes()+1,
1752                                       viewport.alignment));
1753
1754
1755     viewport.setSelectionGroup(null);
1756     viewport.alignment.deleteGroup(sg);
1757
1758     viewport.firePropertyChange("alignment", null,
1759                                   viewport.getAlignment().getSequences());
1760
1761     if (viewport.getAlignment().getHeight() < 1)
1762     {
1763       try
1764       {
1765         this.setClosed(true);
1766       }
1767       catch (Exception ex)
1768       {
1769       }
1770     }
1771   }
1772
1773   /**
1774    * DOCUMENT ME!
1775    *
1776    * @param e DOCUMENT ME!
1777    */
1778   protected void deleteGroups_actionPerformed(ActionEvent e)
1779   {
1780     viewport.alignment.deleteAllGroups();
1781     viewport.sequenceColours = null;
1782     viewport.setSelectionGroup(null);
1783     PaintRefresher.Refresh(this, viewport.getSequenceSetId());
1784     alignPanel.paintAlignment(true);
1785   }
1786
1787   /**
1788    * DOCUMENT ME!
1789    *
1790    * @param e DOCUMENT ME!
1791    */
1792   public void selectAllSequenceMenuItem_actionPerformed(ActionEvent e)
1793   {
1794     SequenceGroup sg = new SequenceGroup();
1795
1796     for (int i = 0; i < viewport.getAlignment().getSequences().size();
1797          i++)
1798     {
1799       sg.addSequence(viewport.getAlignment().getSequenceAt(i), false);
1800     }
1801
1802     sg.setEndRes(viewport.alignment.getWidth() - 1);
1803     viewport.setSelectionGroup(sg);
1804     alignPanel.paintAlignment(true);
1805     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
1806   }
1807
1808   /**
1809    * DOCUMENT ME!
1810    *
1811    * @param e DOCUMENT ME!
1812    */
1813   public void deselectAllSequenceMenuItem_actionPerformed(ActionEvent e)
1814   {
1815     if(viewport.cursorMode)
1816     {
1817       alignPanel.seqPanel.keyboardNo1 = null;
1818       alignPanel.seqPanel.keyboardNo2 = null;
1819     }
1820     viewport.setSelectionGroup(null);
1821     viewport.getColumnSelection().clear();
1822     viewport.setSelectionGroup(null);
1823     alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
1824     alignPanel.idPanel.idCanvas.searchResults = null;
1825     alignPanel.paintAlignment(true);
1826     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
1827   }
1828
1829   /**
1830    * DOCUMENT ME!
1831    *
1832    * @param e DOCUMENT ME!
1833    */
1834   public void invertSequenceMenuItem_actionPerformed(ActionEvent e)
1835   {
1836     SequenceGroup sg = viewport.getSelectionGroup();
1837
1838     if (sg == null)
1839     {
1840       selectAllSequenceMenuItem_actionPerformed(null);
1841
1842       return;
1843     }
1844
1845     for (int i = 0; i < viewport.getAlignment().getSequences().size();
1846          i++)
1847     {
1848       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
1849     }
1850
1851     alignPanel.paintAlignment(true);
1852
1853     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
1854   }
1855
1856   public void invertColSel_actionPerformed(ActionEvent e)
1857   {
1858     viewport.invertColumnSelection();
1859     alignPanel.paintAlignment(true);
1860   }
1861
1862
1863   /**
1864    * DOCUMENT ME!
1865    *
1866    * @param e DOCUMENT ME!
1867    */
1868   public void remove2LeftMenuItem_actionPerformed(ActionEvent e)
1869   {
1870     trimAlignment(true);
1871   }
1872
1873   /**
1874    * DOCUMENT ME!
1875    *
1876    * @param e DOCUMENT ME!
1877    */
1878   public void remove2RightMenuItem_actionPerformed(ActionEvent e)
1879   {
1880       trimAlignment(false);
1881   }
1882
1883   void trimAlignment(boolean trimLeft)
1884   {
1885     ColumnSelection colSel = viewport.getColumnSelection();
1886     int column;
1887
1888     if (colSel.size() > 0)
1889     {
1890       if(trimLeft)
1891       {
1892         column = colSel.getMin();
1893       }
1894       else
1895       {
1896         column = colSel.getMax();
1897       }
1898
1899       SequenceI [] seqs;
1900       if(viewport.getSelectionGroup()!=null)
1901       {
1902         seqs = viewport.getSelectionGroup().getSequencesAsArray(viewport.
1903             hiddenRepSequences);
1904       }
1905       else
1906       {
1907         seqs = viewport.alignment.getSequencesArray();
1908       }
1909
1910
1911       TrimRegionCommand trimRegion;
1912       if(trimLeft)
1913       {
1914         trimRegion = new TrimRegionCommand("Remove Left",
1915                                     TrimRegionCommand.TRIM_LEFT,
1916                                     seqs,
1917                                     column,
1918                                     viewport.alignment,
1919                                     viewport.colSel,
1920                                     viewport.selectionGroup);
1921         viewport.setStartRes(0);
1922       }
1923      else
1924      {
1925        trimRegion = new TrimRegionCommand("Remove Right",
1926                                    TrimRegionCommand.TRIM_RIGHT,
1927                                    seqs,
1928                                    column,
1929                                    viewport.alignment,
1930                                    viewport.colSel,
1931                                    viewport.selectionGroup);
1932      }
1933
1934      statusBar.setText("Removed "+trimRegion.getSize()+" columns.");
1935
1936
1937       addHistoryItem(trimRegion);
1938
1939       Vector groups = viewport.alignment.getGroups();
1940
1941       for (int i = 0; i < groups.size(); i++)
1942       {
1943         SequenceGroup sg = (SequenceGroup) groups.get(i);
1944
1945         if ( (trimLeft && !sg.adjustForRemoveLeft(column))
1946             || (!trimLeft && !sg.adjustForRemoveRight(column)))
1947         {
1948           viewport.alignment.deleteGroup(sg);
1949         }
1950       }
1951
1952       viewport.firePropertyChange("alignment", null,
1953                                   viewport.getAlignment().getSequences());
1954     }
1955   }
1956
1957   /**
1958    * DOCUMENT ME!
1959    *
1960    * @param e DOCUMENT ME!
1961    */
1962   public void removeGappedColumnMenuItem_actionPerformed(ActionEvent e)
1963   {
1964     int start = 0, end = viewport.alignment.getWidth()-1;
1965
1966     SequenceI[] seqs;
1967     if (viewport.getSelectionGroup() != null)
1968     {
1969       seqs = viewport.getSelectionGroup().getSequencesAsArray(viewport.
1970           hiddenRepSequences);
1971       start = viewport.getSelectionGroup().getStartRes();
1972       end = viewport.getSelectionGroup().getEndRes();
1973     }
1974     else
1975     {
1976       seqs = viewport.alignment.getSequencesArray();
1977     }
1978
1979
1980     RemoveGapColCommand removeGapCols =
1981         new RemoveGapColCommand("Remove Gapped Columns",
1982                                 seqs,
1983                                 start, end,
1984                                 viewport.alignment);
1985
1986     addHistoryItem(removeGapCols);
1987
1988     statusBar.setText("Removed "+removeGapCols.getSize()+" empty columns.");
1989
1990     //This is to maintain viewport position on first residue
1991     //of first sequence
1992     SequenceI seq = viewport.alignment.getSequenceAt(0);
1993     int startRes = seq.findPosition(viewport.startRes);
1994    // ShiftList shifts;
1995    // viewport.getAlignment().removeGaps(shifts=new ShiftList());
1996    // edit.alColumnChanges=shifts.getInverse();
1997    // if (viewport.hasHiddenColumns)
1998    //   viewport.getColumnSelection().compensateForEdits(shifts);
1999    viewport.setStartRes(seq.findIndex(startRes)-1);
2000     viewport.firePropertyChange("alignment", null,
2001                                 viewport.getAlignment().getSequences());
2002
2003   }
2004
2005   /**
2006    * DOCUMENT ME!
2007    *
2008    * @param e DOCUMENT ME!
2009    */
2010   public void removeAllGapsMenuItem_actionPerformed(ActionEvent e)
2011   {
2012     int start = 0, end = viewport.alignment.getWidth()-1;
2013
2014     SequenceI[] seqs;
2015     if (viewport.getSelectionGroup() != null)
2016     {
2017       seqs = viewport.getSelectionGroup().getSequencesAsArray(viewport.
2018           hiddenRepSequences);
2019       start = viewport.getSelectionGroup().getStartRes();
2020       end = viewport.getSelectionGroup().getEndRes();
2021     }
2022     else
2023     {
2024       seqs = viewport.alignment.getSequencesArray();
2025     }
2026
2027     //This is to maintain viewport position on first residue
2028     //of first sequence
2029     SequenceI seq = viewport.alignment.getSequenceAt(0);
2030     int startRes = seq.findPosition(viewport.startRes);
2031
2032     addHistoryItem(new RemoveGapsCommand("Remove Gaps",
2033                                          seqs,
2034                                          start, end,
2035                                          viewport.alignment));
2036
2037     viewport.setStartRes(seq.findIndex(startRes)-1);
2038
2039     viewport.firePropertyChange("alignment", null,
2040                                 viewport.getAlignment().getSequences());
2041
2042   }
2043
2044   /**
2045    * DOCUMENT ME!
2046    *
2047    * @param e DOCUMENT ME!
2048    */
2049   public void padGapsMenuitem_actionPerformed(ActionEvent e)
2050   {
2051     viewport.padGaps = padGapsMenuitem.isSelected();
2052
2053     viewport.firePropertyChange("alignment",
2054                                 null,
2055                                 viewport.getAlignment().getSequences());
2056   }
2057
2058   /**
2059    * DOCUMENT ME!
2060    *
2061    * @param e DOCUMENT ME!
2062    */
2063   public void findMenuItem_actionPerformed(ActionEvent e)
2064   {
2065     new Finder();
2066   }
2067
2068   public void newView_actionPerformed(ActionEvent e)
2069   {
2070     AlignmentPanel newap =
2071         new Jalview2XML().copyAlignPanel(alignPanel, true);
2072
2073     newap.av.gatherViewsHere = false;
2074
2075     if (viewport.viewName == null)
2076     {
2077       viewport.viewName = "Original";
2078     }
2079
2080     newap.av.historyList = viewport.historyList;
2081     newap.av.redoList = viewport.redoList;
2082
2083     int index = Desktop.getViewCount(viewport.getSequenceSetId());
2084     String newViewName = "View " +index;
2085
2086     Vector comps = (Vector) PaintRefresher.components.get(viewport.
2087         getSequenceSetId());
2088     Vector existingNames = new Vector();
2089     for(int i=0; i<comps.size(); i++)
2090     {
2091       if(comps.elementAt(i) instanceof AlignmentPanel)
2092       {
2093         AlignmentPanel ap = (AlignmentPanel)comps.elementAt(i);
2094         if(!existingNames.contains(ap.av.viewName))
2095         {
2096           existingNames.addElement(ap.av.viewName);
2097       }
2098     }
2099     }
2100
2101     while(existingNames.contains(newViewName))
2102     {
2103       newViewName = "View "+ (++index);
2104     }
2105
2106     newap.av.viewName = newViewName;
2107
2108     addAlignmentPanel(newap, false);
2109
2110     if(alignPanels.size()==2)
2111     {
2112       viewport.gatherViewsHere = true;
2113     }
2114     tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
2115   }
2116
2117   public void expandViews_actionPerformed(ActionEvent e)
2118   {
2119         Desktop.instance.explodeViews(this);
2120   }
2121
2122   public void gatherViews_actionPerformed(ActionEvent e)
2123   {
2124     Desktop.instance.gatherViews(this);
2125   }
2126
2127
2128
2129   /**
2130    * DOCUMENT ME!
2131    *
2132    * @param e DOCUMENT ME!
2133    */
2134   public void font_actionPerformed(ActionEvent e)
2135   {
2136     new FontChooser(alignPanel);
2137   }
2138
2139
2140   /**
2141    * DOCUMENT ME!
2142    *
2143    * @param e DOCUMENT ME!
2144    */
2145   protected void seqLimit_actionPerformed(ActionEvent e)
2146   {
2147     viewport.setShowJVSuffix(seqLimits.isSelected());
2148
2149     alignPanel.idPanel.idCanvas.setPreferredSize(alignPanel.calculateIdWidth());
2150     alignPanel.paintAlignment(true);
2151   }
2152
2153   public void idRightAlign_actionPerformed(ActionEvent e)
2154   {
2155     viewport.rightAlignIds = idRightAlign.isSelected();
2156     alignPanel.paintAlignment(true);
2157   }
2158
2159
2160
2161   /**
2162    * DOCUMENT ME!
2163    *
2164    * @param e DOCUMENT ME!
2165    */
2166   protected void colourTextMenuItem_actionPerformed(ActionEvent e)
2167   {
2168     viewport.setColourText(colourTextMenuItem.isSelected());
2169     alignPanel.paintAlignment(true);
2170   }
2171
2172   /**
2173    * DOCUMENT ME!
2174    *
2175    * @param e DOCUMENT ME!
2176    */
2177   public void wrapMenuItem_actionPerformed(ActionEvent e)
2178   {
2179     scaleAbove.setVisible(wrapMenuItem.isSelected());
2180     scaleLeft.setVisible(wrapMenuItem.isSelected());
2181     scaleRight.setVisible(wrapMenuItem.isSelected());
2182     viewport.setWrapAlignment(wrapMenuItem.isSelected());
2183     alignPanel.setWrapAlignment(wrapMenuItem.isSelected());
2184   }
2185
2186   public void showAllSeqs_actionPerformed(ActionEvent e)
2187   {
2188     viewport.showAllHiddenSeqs();
2189   }
2190
2191   public void showAllColumns_actionPerformed(ActionEvent e)
2192   {
2193     viewport.showAllHiddenColumns();
2194     repaint();
2195   }
2196
2197   public void hideSelSequences_actionPerformed(ActionEvent e)
2198   {
2199     viewport.hideAllSelectedSeqs();
2200     alignPanel.paintAlignment(true);
2201   }
2202
2203   public void hideSelColumns_actionPerformed(ActionEvent e)
2204   {
2205     viewport.hideSelectedColumns();
2206     alignPanel.paintAlignment(true);
2207   }
2208
2209   public void hiddenMarkers_actionPerformed(ActionEvent e)
2210   {
2211     viewport.setShowHiddenMarkers(hiddenMarkers.isSelected());
2212     repaint();
2213   }
2214
2215   /**
2216    * DOCUMENT ME!
2217    *
2218    * @param e DOCUMENT ME!
2219    */
2220   protected void scaleAbove_actionPerformed(ActionEvent e)
2221   {
2222     viewport.setScaleAboveWrapped(scaleAbove.isSelected());
2223     alignPanel.paintAlignment(true);
2224   }
2225
2226   /**
2227    * DOCUMENT ME!
2228    *
2229    * @param e DOCUMENT ME!
2230    */
2231   protected void scaleLeft_actionPerformed(ActionEvent e)
2232   {
2233     viewport.setScaleLeftWrapped(scaleLeft.isSelected());
2234     alignPanel.paintAlignment(true);
2235   }
2236
2237   /**
2238    * DOCUMENT ME!
2239    *
2240    * @param e DOCUMENT ME!
2241    */
2242   protected void scaleRight_actionPerformed(ActionEvent e)
2243   {
2244     viewport.setScaleRightWrapped(scaleRight.isSelected());
2245     alignPanel.paintAlignment(true);
2246   }
2247
2248   /**
2249    * DOCUMENT ME!
2250    *
2251    * @param e DOCUMENT ME!
2252    */
2253   public void viewBoxesMenuItem_actionPerformed(ActionEvent e)
2254   {
2255     viewport.setShowBoxes(viewBoxesMenuItem.isSelected());
2256     alignPanel.paintAlignment(true);
2257   }
2258
2259   /**
2260    * DOCUMENT ME!
2261    *
2262    * @param e DOCUMENT ME!
2263    */
2264   public void viewTextMenuItem_actionPerformed(ActionEvent e)
2265   {
2266     viewport.setShowText(viewTextMenuItem.isSelected());
2267     alignPanel.paintAlignment(true);
2268   }
2269
2270   /**
2271    * DOCUMENT ME!
2272    *
2273    * @param e DOCUMENT ME!
2274    */
2275   protected void renderGapsMenuItem_actionPerformed(ActionEvent e)
2276   {
2277     viewport.setRenderGaps(renderGapsMenuItem.isSelected());
2278     alignPanel.paintAlignment(true);
2279   }
2280
2281
2282   public FeatureSettings featureSettings;
2283   public void featureSettings_actionPerformed(ActionEvent e)
2284   {
2285     if(featureSettings !=null )
2286     {
2287       featureSettings.close();
2288       featureSettings = null;
2289     }
2290     featureSettings = new FeatureSettings(this);
2291   }
2292
2293   /**
2294    * DOCUMENT ME!
2295    *
2296    * @param evt DOCUMENT ME!
2297    */
2298   public void showSeqFeatures_actionPerformed(ActionEvent evt)
2299   {
2300     viewport.setShowSequenceFeatures(showSeqFeatures.isSelected());
2301     alignPanel.paintAlignment(true);
2302     if (alignPanel.getOverviewPanel() != null)
2303     {
2304       alignPanel.getOverviewPanel().updateOverviewImage();
2305     }
2306   }
2307
2308   /**
2309    * DOCUMENT ME!
2310    *
2311    * @param e DOCUMENT ME!
2312    */
2313   public void annotationPanelMenuItem_actionPerformed(ActionEvent e)
2314   {
2315     viewport.setShowAnnotation(annotationPanelMenuItem.isSelected());
2316     alignPanel.setAnnotationVisible(annotationPanelMenuItem.isSelected());
2317   }
2318
2319   public void alignmentProperties()
2320   {
2321     JEditorPane editPane = new JEditorPane("text/html","");
2322     editPane.setEditable(false);
2323     StringBuffer contents = new StringBuffer("<html>");
2324
2325     float avg  = 0;
2326     int min=Integer.MAX_VALUE, max=0;
2327     for(int i=0; i<viewport.alignment.getHeight(); i++)
2328     {
2329       int size = viewport.alignment.getSequenceAt(i).getEnd()
2330           -viewport.alignment.getSequenceAt(i).getStart();
2331       avg += size;
2332       if(size>max)
2333         max = size;
2334       if(size<min)
2335         min = size;
2336     }
2337     avg = avg/(float)viewport.alignment.getHeight();
2338
2339     contents.append("<br>Sequences: "+ viewport.alignment.getHeight());
2340     contents.append("<br>Minimum Sequence Length: "+min);
2341     contents.append("<br>Maximum Sequence Length: "+max);
2342     contents.append("<br>Average Length: "+(int)avg);
2343
2344     if (((Alignment)viewport.alignment).getProperties() != null)
2345     {
2346       Hashtable props = ((Alignment)viewport.alignment).getProperties();
2347       Enumeration en = props.keys();
2348       contents.append("<br><br><table border=\"1\">");
2349       while(en.hasMoreElements())
2350       {
2351         String key = en.nextElement().toString();
2352         StringBuffer val = new StringBuffer();
2353         String vals = props.get(key).toString();
2354         int pos=0, npos;
2355         do {
2356           npos = vals.indexOf("\n",pos);
2357           if (npos==-1)
2358           {
2359             val.append(vals.substring(pos));
2360           } else {
2361             val.append(vals.substring(pos, npos));
2362             val.append("<br>");
2363           }
2364           pos = npos+1;
2365         } while (npos!=-1);
2366         contents.append("<tr><td>"+key+"</td><td>"+val+"</td></tr>");
2367       }
2368       contents.append("</table>");
2369     }
2370     editPane.setText(contents.toString()+"</html>");
2371     JInternalFrame frame = new JInternalFrame();
2372     frame.getContentPane().add(new JScrollPane(editPane));
2373
2374     Desktop.instance.addInternalFrame(frame,"Alignment Properties: "+getTitle(),500,400);
2375   }
2376
2377
2378   /**
2379    * DOCUMENT ME!
2380    *
2381    * @param e DOCUMENT ME!
2382    */
2383   public void overviewMenuItem_actionPerformed(ActionEvent e)
2384   {
2385     if (alignPanel.overviewPanel != null)
2386     {
2387       return;
2388     }
2389
2390     JInternalFrame frame = new JInternalFrame();
2391     OverviewPanel overview = new OverviewPanel(alignPanel);
2392     frame.setContentPane(overview);
2393     Desktop.addInternalFrame(frame, "Overview " + this.getTitle(),
2394                              frame.getWidth(), frame.getHeight());
2395     frame.pack();
2396     frame.setLayer(JLayeredPane.PALETTE_LAYER);
2397     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
2398     {
2399       public void internalFrameClosed(
2400           javax.swing.event.InternalFrameEvent evt)
2401       {
2402         alignPanel.setOverviewPanel(null);
2403       }
2404       ;
2405     });
2406
2407     alignPanel.setOverviewPanel(overview);
2408   }
2409
2410   public void textColour_actionPerformed(ActionEvent e)
2411   {
2412     new TextColourChooser().chooseColour(alignPanel, null);
2413   }
2414
2415   /**
2416    * DOCUMENT ME!
2417    *
2418    * @param e DOCUMENT ME!
2419    */
2420   protected void noColourmenuItem_actionPerformed(ActionEvent e)
2421   {
2422     changeColour(null);
2423   }
2424
2425   /**
2426    * DOCUMENT ME!
2427    *
2428    * @param e DOCUMENT ME!
2429    */
2430   public void clustalColour_actionPerformed(ActionEvent e)
2431   {
2432     changeColour(new ClustalxColourScheme(
2433         viewport.alignment.getSequences(), viewport.alignment.getWidth()));
2434   }
2435
2436   /**
2437    * DOCUMENT ME!
2438    *
2439    * @param e DOCUMENT ME!
2440    */
2441   public void zappoColour_actionPerformed(ActionEvent e)
2442   {
2443     changeColour(new ZappoColourScheme());
2444   }
2445
2446   /**
2447    * DOCUMENT ME!
2448    *
2449    * @param e DOCUMENT ME!
2450    */
2451   public void taylorColour_actionPerformed(ActionEvent e)
2452   {
2453     changeColour(new TaylorColourScheme());
2454   }
2455
2456   /**
2457    * DOCUMENT ME!
2458    *
2459    * @param e DOCUMENT ME!
2460    */
2461   public void hydrophobicityColour_actionPerformed(ActionEvent e)
2462   {
2463     changeColour(new HydrophobicColourScheme());
2464   }
2465
2466   /**
2467    * DOCUMENT ME!
2468    *
2469    * @param e DOCUMENT ME!
2470    */
2471   public void helixColour_actionPerformed(ActionEvent e)
2472   {
2473     changeColour(new HelixColourScheme());
2474   }
2475
2476   /**
2477    * DOCUMENT ME!
2478    *
2479    * @param e DOCUMENT ME!
2480    */
2481   public void strandColour_actionPerformed(ActionEvent e)
2482   {
2483     changeColour(new StrandColourScheme());
2484   }
2485
2486   /**
2487    * DOCUMENT ME!
2488    *
2489    * @param e DOCUMENT ME!
2490    */
2491   public void turnColour_actionPerformed(ActionEvent e)
2492   {
2493     changeColour(new TurnColourScheme());
2494   }
2495
2496   /**
2497    * DOCUMENT ME!
2498    *
2499    * @param e DOCUMENT ME!
2500    */
2501   public void buriedColour_actionPerformed(ActionEvent e)
2502   {
2503     changeColour(new BuriedColourScheme());
2504   }
2505
2506   /**
2507    * DOCUMENT ME!
2508    *
2509    * @param e DOCUMENT ME!
2510    */
2511   public void nucleotideColour_actionPerformed(ActionEvent e)
2512   {
2513     changeColour(new NucleotideColourScheme());
2514   }
2515
2516   public void annotationColour_actionPerformed(ActionEvent e)
2517   {
2518     new AnnotationColourChooser(viewport, alignPanel);
2519   }
2520
2521
2522   /**
2523    * DOCUMENT ME!
2524    *
2525    * @param e DOCUMENT ME!
2526    */
2527   protected void applyToAllGroups_actionPerformed(ActionEvent e)
2528   {
2529     viewport.setColourAppliesToAllGroups(applyToAllGroups.isSelected());
2530   }
2531
2532   /**
2533    * DOCUMENT ME!
2534    *
2535    * @param cs DOCUMENT ME!
2536    */
2537   public void changeColour(ColourSchemeI cs)
2538   {
2539     int threshold = 0;
2540
2541     if(cs!=null)
2542     {
2543       if (viewport.getAbovePIDThreshold())
2544       {
2545         threshold = SliderPanel.setPIDSliderSource(alignPanel, cs,
2546                                                    "Background");
2547
2548         cs.setThreshold(threshold,
2549                         viewport.getIgnoreGapsConsensus());
2550
2551         viewport.setGlobalColourScheme(cs);
2552       }
2553       else
2554       {
2555         cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
2556       }
2557
2558       if (viewport.getConservationSelected())
2559       {
2560
2561         Alignment al = (Alignment) viewport.alignment;
2562         Conservation c = new Conservation("All",
2563                                           ResidueProperties.propHash, 3,
2564                                           al.getSequences(), 0,
2565                                           al.getWidth() - 1);
2566
2567         c.calculate();
2568         c.verdict(false, viewport.ConsPercGaps);
2569
2570         cs.setConservation(c);
2571
2572         cs.setConservationInc(SliderPanel.setConservationSlider(alignPanel, cs,
2573             "Background"));
2574       }
2575       else
2576       {
2577         cs.setConservation(null);
2578       }
2579
2580       cs.setConsensus(viewport.hconsensus);
2581     }
2582
2583     viewport.setGlobalColourScheme(cs);
2584
2585     if (viewport.getColourAppliesToAllGroups())
2586     {
2587       Vector groups = viewport.alignment.getGroups();
2588
2589       for (int i = 0; i < groups.size(); i++)
2590       {
2591         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
2592
2593         if (cs == null)
2594         {
2595           sg.cs = null;
2596           continue;
2597         }
2598
2599         if (cs instanceof ClustalxColourScheme)
2600         {
2601           sg.cs = new ClustalxColourScheme(
2602               sg.getSequences(viewport.hiddenRepSequences), sg.getWidth());
2603         }
2604         else if (cs instanceof UserColourScheme)
2605         {
2606           sg.cs = new UserColourScheme( ( (UserColourScheme) cs).getColours());
2607         }
2608         else
2609         {
2610           try
2611           {
2612             sg.cs = (ColourSchemeI) cs.getClass().newInstance();
2613           }
2614           catch (Exception ex)
2615           {
2616           }
2617         }
2618
2619         if (viewport.getAbovePIDThreshold()
2620             || cs instanceof PIDColourScheme
2621             || cs instanceof Blosum62ColourScheme)
2622         {
2623          sg.cs.setThreshold(threshold,
2624                 viewport.getIgnoreGapsConsensus());
2625
2626          sg.cs.setConsensus(AAFrequency.calculate(
2627              sg.getSequences(viewport.hiddenRepSequences), sg.getStartRes(),
2628              sg.getEndRes()+1));
2629        }
2630         else
2631         {
2632           sg.cs.setThreshold(0, viewport.getIgnoreGapsConsensus());
2633         }
2634
2635
2636         if (viewport.getConservationSelected())
2637         {
2638           Conservation c = new Conservation("Group",
2639                                             ResidueProperties.propHash, 3,
2640                                             sg.getSequences(viewport.
2641               hiddenRepSequences),
2642                                             sg.getStartRes(),
2643                                             sg.getEndRes()+1);
2644           c.calculate();
2645           c.verdict(false, viewport.ConsPercGaps);
2646           sg.cs.setConservation(c);
2647         }
2648         else
2649         {
2650           sg.cs.setConservation(null);
2651       }
2652     }
2653     }
2654
2655     if (alignPanel.getOverviewPanel() != null)
2656     {
2657       alignPanel.getOverviewPanel().updateOverviewImage();
2658     }
2659
2660
2661
2662
2663     alignPanel.paintAlignment(true);
2664   }
2665
2666   /**
2667    * DOCUMENT ME!
2668    *
2669    * @param e DOCUMENT ME!
2670    */
2671   protected void modifyPID_actionPerformed(ActionEvent e)
2672   {
2673     if (viewport.getAbovePIDThreshold() && viewport.globalColourScheme!=null)
2674     {
2675       SliderPanel.setPIDSliderSource(alignPanel,
2676                                      viewport.getGlobalColourScheme(),
2677                                      "Background");
2678       SliderPanel.showPIDSlider();
2679     }
2680   }
2681
2682   /**
2683    * DOCUMENT ME!
2684    *
2685    * @param e DOCUMENT ME!
2686    */
2687   protected void modifyConservation_actionPerformed(ActionEvent e)
2688   {
2689     if (viewport.getConservationSelected() && viewport.globalColourScheme!=null)
2690     {
2691       SliderPanel.setConservationSlider(alignPanel,
2692                                         viewport.globalColourScheme,
2693                                         "Background");
2694       SliderPanel.showConservationSlider();
2695     }
2696   }
2697
2698   /**
2699    * DOCUMENT ME!
2700    *
2701    * @param e DOCUMENT ME!
2702    */
2703   protected void conservationMenuItem_actionPerformed(ActionEvent e)
2704   {
2705     viewport.setConservationSelected(conservationMenuItem.isSelected());
2706
2707     viewport.setAbovePIDThreshold(false);
2708     abovePIDThreshold.setSelected(false);
2709
2710     changeColour(viewport.getGlobalColourScheme());
2711
2712     modifyConservation_actionPerformed(null);
2713   }
2714
2715   /**
2716    * DOCUMENT ME!
2717    *
2718    * @param e DOCUMENT ME!
2719    */
2720   public void abovePIDThreshold_actionPerformed(ActionEvent e)
2721   {
2722     viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
2723
2724     conservationMenuItem.setSelected(false);
2725     viewport.setConservationSelected(false);
2726
2727     changeColour(viewport.getGlobalColourScheme());
2728
2729     modifyPID_actionPerformed(null);
2730   }
2731
2732   /**
2733    * DOCUMENT ME!
2734    *
2735    * @param e DOCUMENT ME!
2736    */
2737   public void userDefinedColour_actionPerformed(ActionEvent e)
2738   {
2739     if (e.getActionCommand().equals("User Defined..."))
2740     {
2741       new UserDefinedColours(alignPanel, null);
2742     }
2743     else
2744     {
2745       UserColourScheme udc = (UserColourScheme) UserDefinedColours.
2746           getUserColourSchemes().get(e.getActionCommand());
2747
2748       changeColour(udc);
2749     }
2750   }
2751
2752   public void updateUserColourMenu()
2753   {
2754
2755     Component[] menuItems = colourMenu.getMenuComponents();
2756     int i, iSize = menuItems.length;
2757     for (i = 0; i < iSize; i++)
2758     {
2759       if (menuItems[i].getName() != null &&
2760           menuItems[i].getName().equals("USER_DEFINED"))
2761       {
2762         colourMenu.remove(menuItems[i]);
2763         iSize--;
2764       }
2765     }
2766     if (jalview.gui.UserDefinedColours.getUserColourSchemes() != null)
2767     {
2768       java.util.Enumeration userColours = jalview.gui.UserDefinedColours.
2769           getUserColourSchemes().keys();
2770
2771       while (userColours.hasMoreElements())
2772       {
2773         final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
2774             userColours.
2775             nextElement().toString());
2776         radioItem.setName("USER_DEFINED");
2777         radioItem.addMouseListener(new MouseAdapter()
2778             {
2779               public void mousePressed(MouseEvent evt)
2780               {
2781                 if(evt.isControlDown() || SwingUtilities.isRightMouseButton(evt))
2782                 {
2783                   radioItem.removeActionListener(radioItem.getActionListeners()[0]);
2784
2785               int option = JOptionPane.showInternalConfirmDialog(jalview.gui.
2786                   Desktop.desktop,
2787                       "Remove from default list?",
2788                       "Remove user defined colour",
2789                       JOptionPane.YES_NO_OPTION);
2790                   if(option == JOptionPane.YES_OPTION)
2791                   {
2792                 jalview.gui.UserDefinedColours.removeColourFromDefaults(
2793                     radioItem.getText());
2794                     colourMenu.remove(radioItem);
2795                   }
2796                   else
2797               {
2798                     radioItem.addActionListener(new ActionListener()
2799                     {
2800                       public void actionPerformed(ActionEvent evt)
2801                       {
2802                         userDefinedColour_actionPerformed(evt);
2803                       }
2804                     });
2805                 }
2806               }
2807           }
2808             });
2809         radioItem.addActionListener(new ActionListener()
2810         {
2811           public void actionPerformed(ActionEvent evt)
2812           {
2813             userDefinedColour_actionPerformed(evt);
2814           }
2815         });
2816
2817         colourMenu.insert(radioItem, 15);
2818         colours.add(radioItem);
2819       }
2820     }
2821   }
2822
2823   /**
2824    * DOCUMENT ME!
2825    *
2826    * @param e DOCUMENT ME!
2827    */
2828   public void PIDColour_actionPerformed(ActionEvent e)
2829   {
2830     changeColour(new PIDColourScheme());
2831   }
2832
2833   /**
2834    * DOCUMENT ME!
2835    *
2836    * @param e DOCUMENT ME!
2837    */
2838   public void BLOSUM62Colour_actionPerformed(ActionEvent e)
2839   {
2840     changeColour(new Blosum62ColourScheme());
2841   }
2842
2843   /**
2844    * DOCUMENT ME!
2845    *
2846    * @param e DOCUMENT ME!
2847    */
2848   public void sortPairwiseMenuItem_actionPerformed(ActionEvent e)
2849   {
2850     SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
2851     AlignmentSorter.sortByPID(viewport.getAlignment(),
2852                               viewport.getAlignment().getSequenceAt(0));
2853     addHistoryItem(new OrderCommand("Pairwise Sort", oldOrder,
2854                                     viewport.alignment));
2855     alignPanel.paintAlignment(true);
2856   }
2857
2858   /**
2859    * DOCUMENT ME!
2860    *
2861    * @param e DOCUMENT ME!
2862    */
2863   public void sortIDMenuItem_actionPerformed(ActionEvent e)
2864   {
2865     SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
2866     AlignmentSorter.sortByID(viewport.getAlignment());
2867     addHistoryItem(new OrderCommand("ID Sort", oldOrder, viewport.alignment));
2868     alignPanel.paintAlignment(true);
2869   }
2870
2871   /**
2872    * DOCUMENT ME!
2873    *
2874    * @param e DOCUMENT ME!
2875    */
2876   public void sortGroupMenuItem_actionPerformed(ActionEvent e)
2877   {
2878     SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
2879     AlignmentSorter.sortByGroup(viewport.getAlignment());
2880     addHistoryItem(new OrderCommand("Group Sort", oldOrder, viewport.alignment));
2881
2882     alignPanel.paintAlignment(true);
2883   }
2884   /**
2885    * DOCUMENT ME!
2886    *
2887    * @param e DOCUMENT ME!
2888    */
2889   public void removeRedundancyMenuItem_actionPerformed(ActionEvent e)
2890   {
2891     new RedundancyPanel(alignPanel, this);
2892   }
2893
2894
2895   /**
2896    * DOCUMENT ME!
2897    *
2898    * @param e DOCUMENT ME!
2899    */
2900   public void pairwiseAlignmentMenuItem_actionPerformed(ActionEvent e)
2901   {
2902     if ( (viewport.getSelectionGroup() == null) ||
2903         (viewport.getSelectionGroup().getSize() < 2))
2904     {
2905       JOptionPane.showInternalMessageDialog(this,
2906                                             "You must select at least 2 sequences.",
2907                                             "Invalid Selection",
2908                                             JOptionPane.WARNING_MESSAGE);
2909     }
2910     else
2911     {
2912       JInternalFrame frame = new JInternalFrame();
2913       frame.setContentPane(new PairwiseAlignPanel(viewport));
2914       Desktop.addInternalFrame(frame, "Pairwise Alignment", 600, 500);
2915     }
2916   }
2917
2918   /**
2919    * DOCUMENT ME!
2920    *
2921    * @param e DOCUMENT ME!
2922    */
2923   public void PCAMenuItem_actionPerformed(ActionEvent e)
2924   {
2925     if ( ( (viewport.getSelectionGroup() != null) &&
2926           (viewport.getSelectionGroup().getSize() < 4) &&
2927           (viewport.getSelectionGroup().getSize() > 0)) ||
2928         (viewport.getAlignment().getHeight() < 4))
2929     {
2930       JOptionPane.showInternalMessageDialog(this,
2931                                             "Principal component analysis must take\n" +
2932                                             "at least 4 input sequences.",
2933                                             "Sequence selection insufficient",
2934                                             JOptionPane.WARNING_MESSAGE);
2935
2936       return;
2937     }
2938
2939      new PCAPanel(alignPanel);
2940   }
2941
2942
2943   public void autoCalculate_actionPerformed(ActionEvent e)
2944   {
2945     viewport.autoCalculateConsensus = autoCalculate.isSelected();
2946     if(viewport.autoCalculateConsensus)
2947     {
2948       viewport.firePropertyChange("alignment",
2949                                   null,
2950                                   viewport.getAlignment().getSequences());
2951     }
2952   }
2953
2954
2955   /**
2956    * DOCUMENT ME!
2957    *
2958    * @param e DOCUMENT ME!
2959    */
2960   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
2961   {
2962     NewTreePanel("AV", "PID", "Average distance tree using PID");
2963   }
2964
2965   /**
2966    * DOCUMENT ME!
2967    *
2968    * @param e DOCUMENT ME!
2969    */
2970   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
2971   {
2972     NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
2973   }
2974
2975   /**
2976    * DOCUMENT ME!
2977    *
2978    * @param e DOCUMENT ME!
2979    */
2980   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
2981   {
2982     NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
2983   }
2984
2985   /**
2986    * DOCUMENT ME!
2987    *
2988    * @param e DOCUMENT ME!
2989    */
2990   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
2991   {
2992     NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
2993   }
2994
2995   /**
2996    * DOCUMENT ME!
2997    *
2998    * @param type DOCUMENT ME!
2999    * @param pwType DOCUMENT ME!
3000    * @param title DOCUMENT ME!
3001    */
3002   void NewTreePanel(String type, String pwType, String title)
3003   {
3004     TreePanel tp;
3005
3006     if (viewport.getSelectionGroup() != null)
3007     {
3008       if (viewport.getSelectionGroup().getSize() < 3)
3009       {
3010         JOptionPane.showMessageDialog(Desktop.desktop,
3011                                       "You need to have more than two sequences selected to build a tree!",
3012                                       "Not enough sequences",
3013                                       JOptionPane.WARNING_MESSAGE);
3014         return;
3015       }
3016
3017       int s = 0;
3018       SequenceGroup sg = viewport.getSelectionGroup();
3019
3020       /* Decide if the selection is a column region */
3021       while (s < sg.getSize())
3022       {
3023         if ( ( (SequenceI) sg.getSequences(null).elementAt(s++)).getLength() <
3024             sg.getEndRes())
3025         {
3026           JOptionPane.showMessageDialog(Desktop.desktop,
3027                                         "The selected region to create a tree may\nonly contain residues or gaps.\n" +
3028                                         "Try using the Pad function in the edit menu,\n" +
3029                                         "or one of the multiple sequence alignment web services.",
3030                                         "Sequences in selection are not aligned",
3031                                         JOptionPane.WARNING_MESSAGE);
3032
3033           return;
3034         }
3035       }
3036
3037       title = title + " on region";
3038       tp = new TreePanel(alignPanel, type, pwType);
3039     }
3040     else
3041     {
3042       //are the sequences aligned?
3043       if (!viewport.alignment.isAligned())
3044       {
3045         JOptionPane.showMessageDialog(Desktop.desktop,
3046                                       "The sequences must be aligned before creating a tree.\n" +
3047                                       "Try using the Pad function in the edit menu,\n" +
3048                                       "or one of the multiple sequence alignment web services.",
3049                                       "Sequences not aligned",
3050                                       JOptionPane.WARNING_MESSAGE);
3051
3052         return;
3053       }
3054
3055       if(viewport.alignment.getHeight()<2)
3056       {
3057         return;
3058       }
3059
3060       tp = new TreePanel(alignPanel, type, pwType);
3061     }
3062
3063     title += " from ";
3064
3065     if(viewport.viewName!=null)
3066     {
3067       title+= viewport.viewName+" of ";
3068     }
3069
3070     title += this.title;
3071
3072     Desktop.addInternalFrame(tp, title, 600, 500);
3073   }
3074
3075   /**
3076    * DOCUMENT ME!
3077    *
3078    * @param title DOCUMENT ME!
3079    * @param order DOCUMENT ME!
3080    */
3081   public void addSortByOrderMenuItem(String title, final AlignmentOrder order)
3082   {
3083     final JMenuItem item = new JMenuItem("by " + title);
3084     sort.add(item);
3085     item.addActionListener(new java.awt.event.ActionListener()
3086     {
3087       public void actionPerformed(ActionEvent e)
3088       {
3089         SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
3090
3091         // TODO: JBPNote - have to map order entries to curent SequenceI pointers
3092         AlignmentSorter.sortBy(viewport.getAlignment(), order);
3093
3094         addHistoryItem(new OrderCommand(order.getName(), oldOrder,
3095                                         viewport.alignment));
3096
3097         alignPanel.paintAlignment(true);
3098       }
3099     });
3100   }
3101   /**
3102    * Add a new sort by annotation score menu item
3103    * @param sort the menu to add the option to
3104    * @param scoreLabel the label used to retrieve scores for each sequence on the alignment
3105    */
3106   public void addSortByAnnotScoreMenuItem(JMenu sort, final String scoreLabel)
3107   {
3108     final JMenuItem item = new JMenuItem(scoreLabel);
3109     sort.add(item);
3110     item.addActionListener(new java.awt.event.ActionListener()
3111     {
3112       public void actionPerformed(ActionEvent e)
3113       {
3114         SequenceI [] oldOrder = viewport.getAlignment().getSequencesArray();
3115         AlignmentSorter.sortByAnnotationScore(scoreLabel, viewport.getAlignment());
3116         addHistoryItem(new OrderCommand("Sort by "+scoreLabel, oldOrder, viewport.alignment));
3117         alignPanel.paintAlignment(true);
3118       }
3119     });
3120   }
3121   /**
3122    * last hash for alignment's annotation array - used to minimise cost of rebuild.
3123    */
3124   protected int _annotationScoreVectorHash;
3125   /**
3126    * search the alignment and rebuild the sort by annotation score submenu
3127    * the last alignment annotation vector hash is stored to minimize
3128    * cost of rebuilding in subsequence calls.
3129    *
3130    */
3131   public void buildSortByAnnotationScoresMenu()
3132   {
3133     if(viewport.alignment.getAlignmentAnnotation()==null)
3134     {
3135       return;
3136     }
3137
3138     if (viewport.alignment.getAlignmentAnnotation().hashCode()!=_annotationScoreVectorHash)
3139     {
3140       sortByAnnotScore.removeAll();
3141       // almost certainly a quicker way to do this - but we keep it simple
3142       Hashtable scoreSorts=new Hashtable();
3143       AlignmentAnnotation aann[];
3144       Enumeration sq = viewport.alignment.getSequences().elements();
3145       while (sq.hasMoreElements())
3146       {
3147         aann = ((SequenceI) sq.nextElement()).getAnnotation();
3148         for (int i=0;aann!=null && i<aann.length; i++)
3149         {
3150           if (aann[i].hasScore() && aann[i].sequenceRef!=null)
3151           {
3152             scoreSorts.put(aann[i].label, aann[i].label);
3153           }
3154         }
3155       }
3156       Enumeration labels = scoreSorts.keys();
3157       while (labels.hasMoreElements())
3158       {
3159         addSortByAnnotScoreMenuItem(sortByAnnotScore, (String) labels.nextElement());
3160       }
3161       sortByAnnotScore.setVisible(scoreSorts.size()>0);
3162       scoreSorts.clear();
3163
3164       _annotationScoreVectorHash =
3165           viewport.alignment.getAlignmentAnnotation().hashCode();
3166     }
3167   }
3168
3169   /**
3170    * Maintain the Order by->Displayed Tree menu.
3171    * Creates a new menu item for a TreePanel with an appropriate
3172    * <code>jalview.analysis.AlignmentSorter</code> call. Listeners are added
3173    * to remove the menu item when the treePanel is closed, and adjust
3174    * the tree leaf to sequence mapping when the alignment is modified.
3175    * @param treePanel Displayed tree window.
3176    * @param title SortBy menu item title.
3177    */
3178   public void buildTreeMenu()
3179   {
3180     sortByTreeMenu.removeAll();
3181
3182     Vector comps = (Vector) PaintRefresher.components.get(viewport.
3183         getSequenceSetId());
3184     Vector treePanels = new Vector();
3185     int i, iSize = comps.size();
3186     for(i=0; i<iSize; i++)
3187     {
3188       if(comps.elementAt(i) instanceof TreePanel)
3189       {
3190         treePanels.add(comps.elementAt(i));
3191       }
3192     }
3193
3194     iSize = treePanels.size();
3195
3196     if(iSize<1)
3197     {
3198       sortByTreeMenu.setVisible(false);
3199       return;
3200     }
3201
3202     sortByTreeMenu.setVisible(true);
3203
3204     for(i=0; i<treePanels.size(); i++)
3205     {
3206       TreePanel tp = (TreePanel)treePanels.elementAt(i);
3207       final JMenuItem item = new JMenuItem(tp.getTitle());
3208       final NJTree tree = ((TreePanel)treePanels.elementAt(i)).getTree();
3209       item.addActionListener(new java.awt.event.ActionListener()
3210       {
3211         public void actionPerformed(ActionEvent e)
3212         {
3213           SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
3214           AlignmentSorter.sortByTree(viewport.getAlignment(), tree);
3215
3216           addHistoryItem(new OrderCommand("Tree Sort",
3217                                           oldOrder,
3218                                           viewport.alignment));
3219
3220           alignPanel.paintAlignment(true);
3221         }
3222       });
3223
3224       sortByTreeMenu.add(item);
3225     }
3226   }
3227
3228   /**
3229    * Work out whether the whole set of sequences
3230    * or just the selected set will be submitted for multiple alignment.
3231    *
3232    */
3233   private jalview.datamodel.AlignmentView gatherSequencesForAlignment()
3234   {
3235     // Now, check we have enough sequences
3236     AlignmentView msa = null;
3237
3238     if ( (viewport.getSelectionGroup() != null) &&
3239         (viewport.getSelectionGroup().getSize() > 1))
3240     {
3241       // JBPNote UGLY! To prettify, make SequenceGroup and Alignment conform to some common interface!
3242       /*SequenceGroup seqs = viewport.getSelectionGroup();
3243       int sz;
3244       msa = new SequenceI[sz = seqs.getSize(false)];
3245
3246       for (int i = 0; i < sz; i++)
3247       {
3248         msa[i] = (SequenceI) seqs.getSequenceAt(i);
3249       } */
3250       msa = viewport.getAlignmentView(true);
3251     }
3252     else
3253     {
3254       /*Vector seqs = viewport.getAlignment().getSequences();
3255
3256       if (seqs.size() > 1)
3257       {
3258         msa = new SequenceI[seqs.size()];
3259
3260         for (int i = 0; i < seqs.size(); i++)
3261         {
3262           msa[i] = (SequenceI) seqs.elementAt(i);
3263         }
3264       }*/
3265       msa = viewport.getAlignmentView(false);
3266     }
3267     return msa;
3268   }
3269
3270   /**
3271    * Decides what is submitted to a secondary structure prediction service,
3272    * the currently selected sequence, or the currently selected alignment
3273    * (where the first sequence in the set is the one that the prediction
3274    * will be for).
3275    */
3276   AlignmentView gatherSeqOrMsaForSecStrPrediction()
3277   {
3278    AlignmentView seqs = null;
3279
3280     if ( (viewport.getSelectionGroup() != null) &&
3281         (viewport.getSelectionGroup().getSize() > 0))
3282     {
3283       seqs = viewport.getAlignmentView(true);
3284     }
3285     else
3286     {
3287       seqs = viewport.getAlignmentView(false);
3288     }
3289     // limit sequences - JBPNote in future - could spawn multiple prediction jobs
3290     // TODO: viewport.alignment.isAligned is a global state - the local selection may well be aligned - we preserve 2.0.8 behaviour for moment.
3291     if (!viewport.alignment.isAligned())
3292     {
3293       seqs.setSequences(new SeqCigar[]
3294                         {seqs.getSequences()[0]});
3295     }
3296     return seqs;
3297   }
3298   /**
3299    * DOCUMENT ME!
3300    *
3301    * @param e DOCUMENT ME!
3302    */
3303   protected void LoadtreeMenuItem_actionPerformed(ActionEvent e)
3304   {
3305     // Pick the tree file
3306     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
3307         getProperty(
3308             "LAST_DIRECTORY"));
3309     chooser.setFileView(new JalviewFileView());
3310     chooser.setDialogTitle("Select a newick-like tree file");
3311     chooser.setToolTipText("Load a tree file");
3312
3313     int value = chooser.showOpenDialog(null);
3314
3315     if (value == JalviewFileChooser.APPROVE_OPTION)
3316     {
3317       String choice = chooser.getSelectedFile().getPath();
3318       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice);
3319
3320       try
3321       {
3322         jalview.io.NewickFile fin = new jalview.io.NewickFile(choice,
3323             "File");
3324         viewport.setCurrentTree(ShowNewickTree(fin, choice).getTree());
3325       }
3326       catch (Exception ex)
3327       {
3328         JOptionPane.showMessageDialog(Desktop.desktop,
3329                                       "Problem reading tree file",
3330                                       ex.getMessage(),
3331                                       JOptionPane.WARNING_MESSAGE);
3332         ex.printStackTrace();
3333       }
3334     }
3335   }
3336
3337
3338   public TreePanel ShowNewickTree(NewickFile nf, String title)
3339   {
3340     return ShowNewickTree(nf,title,600,500,4,5);
3341   }
3342
3343   public TreePanel ShowNewickTree(NewickFile nf, String title,
3344                                   AlignmentView input)
3345   {
3346     return ShowNewickTree(nf,title, input, 600,500,4,5);
3347   }
3348
3349   public TreePanel ShowNewickTree(NewickFile nf, String title, int w, int h,
3350                                   int x, int y)
3351   {
3352     return ShowNewickTree(nf, title, null, w, h, x, y);
3353   }
3354   /**
3355    * Add a treeviewer for the tree extracted from a newick file object to the current alignment view
3356    *
3357    * @param nf the tree
3358    * @param title tree viewer title
3359    * @param input Associated alignment input data (or null)
3360    * @param w width
3361    * @param h height
3362    * @param x position
3363    * @param y position
3364    * @return TreePanel handle
3365    */
3366   public TreePanel ShowNewickTree(NewickFile nf, String title,
3367                                   AlignmentView input, int w, int h, int x,
3368                                   int y)
3369   {
3370     TreePanel tp = null;
3371
3372     try
3373     {
3374       nf.parse();
3375
3376       if (nf.getTree() != null)
3377       {
3378         tp = new TreePanel(alignPanel,
3379                            "FromFile",
3380                            title,
3381                            nf, input);
3382
3383         tp.setSize(w,h);
3384
3385         if(x>0 && y>0)
3386         {
3387           tp.setLocation(x,y);
3388         }
3389
3390
3391         Desktop.addInternalFrame(tp, title, w, h);
3392       }
3393     }
3394     catch (Exception ex)
3395     {
3396       ex.printStackTrace();
3397     }
3398
3399     return tp;
3400   }
3401
3402
3403   /**
3404    * Generates menu items and listener event actions for web service clients
3405    *
3406    */
3407   public void BuildWebServiceMenu()
3408   {
3409     if ( (Discoverer.services != null)
3410         && (Discoverer.services.size() > 0))
3411     {
3412       Vector msaws = (Vector) Discoverer.services.get("MsaWS");
3413       Vector secstrpr = (Vector) Discoverer.services.get("SecStrPred");
3414       Vector wsmenu = new Vector();
3415       final AlignFrame af = this;
3416       if (msaws != null)
3417       {
3418         // Add any Multiple Sequence Alignment Services
3419         final JMenu msawsmenu = new JMenu("Alignment");
3420         for (int i = 0, j = msaws.size(); i < j; i++)
3421         {
3422           final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle) msaws.
3423               get(i);
3424           final JMenuItem method = new JMenuItem(sh.getName());
3425           method.addActionListener(new ActionListener()
3426           {
3427             public void actionPerformed(ActionEvent e)
3428             {
3429               AlignmentView msa = gatherSequencesForAlignment();
3430               new jalview.ws.MsaWSClient(sh, title, msa,
3431                                          false, true,
3432                                          viewport.getAlignment().getDataset(),
3433                                          af);
3434
3435             }
3436
3437           });
3438           msawsmenu.add(method);
3439           // Deal with services that we know accept partial alignments.
3440           if (sh.getName().indexOf("lustal") > -1)
3441           {
3442             // We know that ClustalWS can accept partial alignments for refinement.
3443             final JMenuItem methodR = new JMenuItem(sh.getName()+" Realign");
3444             methodR.addActionListener(new ActionListener()
3445             {
3446               public void actionPerformed(ActionEvent e)
3447               {
3448                 AlignmentView msa = gatherSequencesForAlignment();
3449                 new jalview.ws.MsaWSClient(sh, title, msa,
3450                                            true, true,
3451                                            viewport.getAlignment().getDataset(),
3452                                            af);
3453
3454               }
3455
3456             });
3457             msawsmenu.add(methodR);
3458
3459           }
3460         }
3461         wsmenu.add(msawsmenu);
3462       }
3463       if (secstrpr != null)
3464       {
3465         // Add any secondary structure prediction services
3466         final JMenu secstrmenu = new JMenu("Secondary Structure Prediction");
3467         for (int i = 0, j = secstrpr.size(); i < j; i++)
3468         {
3469           final ext.vamsas.ServiceHandle sh = (ext.vamsas.ServiceHandle)
3470               secstrpr.get(i);
3471           final JMenuItem method = new JMenuItem(sh.getName());
3472           method.addActionListener(new ActionListener()
3473           {
3474             public void actionPerformed(ActionEvent e)
3475             {
3476               AlignmentView msa = gatherSeqOrMsaForSecStrPrediction();
3477               if (msa.getSequences().length == 1)
3478               {
3479                 // Single Sequence prediction
3480                 new jalview.ws.JPredClient(sh, title, false, msa, af, true);
3481               }
3482               else
3483               {
3484                 if (msa.getSequences().length > 1)
3485                 {
3486                   // Sequence profile based prediction
3487                   new jalview.ws.JPredClient(sh,
3488                       title, true, msa, af, true);
3489                 }
3490               }
3491             }
3492           });
3493           secstrmenu.add(method);
3494         }
3495         wsmenu.add(secstrmenu);
3496       }
3497       resetWebServiceMenu();
3498       for (int i = 0, j = wsmenu.size(); i < j; i++)
3499       {
3500         webService.add( (JMenu) wsmenu.get(i));
3501       }
3502     }
3503     else
3504     {
3505       resetWebServiceMenu();
3506       this.webService.add(this.webServiceNoServices);
3507     }
3508     // TODO: add in rediscovery function
3509     // TODO: reduce code redundancy.
3510     // TODO: group services by location as well as function.
3511   }
3512
3513
3514   /**
3515    * empty the web service menu and add any ad-hoc functions
3516    * not dynamically discovered.
3517    *
3518    */
3519   private void resetWebServiceMenu()
3520   {
3521     webService.removeAll();
3522     // Temporary hack - DBRef Fetcher always top level ws entry.
3523     JMenuItem rfetch = new JMenuItem("Fetch DB References");
3524     rfetch.setToolTipText("Retrieve and parse uniprot records for the alignment or the currently selected sequences");
3525     webService.add(rfetch);
3526     rfetch.addActionListener(new ActionListener() {
3527
3528       public void actionPerformed(ActionEvent e)
3529       {
3530         new jalview.io.DBRefFetcher(
3531                 alignPanel.av.getSequenceSelection(),
3532                 alignPanel.alignFrame).fetchDBRefs(false);
3533       }
3534
3535     });
3536   }
3537
3538  /* public void vamsasStore_actionPerformed(ActionEvent e)
3539   {
3540     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
3541         getProperty("LAST_DIRECTORY"));
3542
3543     chooser.setFileView(new JalviewFileView());
3544     chooser.setDialogTitle("Export to Vamsas file");
3545     chooser.setToolTipText("Export");
3546
3547     int value = chooser.showSaveDialog(this);
3548
3549     if (value == JalviewFileChooser.APPROVE_OPTION)
3550     {
3551       jalview.io.VamsasDatastore vs = new jalview.io.VamsasDatastore(viewport);
3552       //vs.store(chooser.getSelectedFile().getAbsolutePath()   );
3553       vs.storeJalview( chooser.getSelectedFile().getAbsolutePath(), this);
3554     }
3555   }*/
3556
3557
3558
3559 public void showTranslation_actionPerformed(ActionEvent e)
3560 {
3561   ///////////////////////////////
3562   // Collect Data to be translated/transferred
3563
3564   SequenceI [] selection = viewport.getSequenceSelection();
3565   String [] seqstring = viewport.getViewAsString(true);
3566   AlignmentI al  = null;
3567   try {
3568     al = jalview.analysis.Dna.CdnaTranslate(selection, seqstring, viewport.getViewAsVisibleContigs(true),
3569         viewport.getGapCharacter(), viewport.alignment.getAlignmentAnnotation(),
3570         viewport.alignment.getWidth());
3571   } catch (Exception ex) {
3572     al = null;
3573     jalview.bin.Cache.log.debug("Exception during translation.",ex);
3574   }
3575   if (al==null)
3576   {
3577     JOptionPane.showMessageDialog(Desktop.desktop,
3578         "Please select at least three bases in at least one sequence in order to perform a cDNA translation.",
3579         "Translation Failed",
3580         JOptionPane.WARNING_MESSAGE);
3581   } else {
3582     AlignFrame af = new AlignFrame(al, DEFAULT_WIDTH, DEFAULT_HEIGHT);
3583     Desktop.addInternalFrame(af, "Translation of "+this.getTitle(),
3584                              DEFAULT_WIDTH,
3585                              DEFAULT_HEIGHT);
3586   }
3587 }
3588
3589 /**
3590  * DOCUMENT ME!
3591  *
3592  * @param String DOCUMENT ME!
3593  */
3594 public boolean parseFeaturesFile(String file, String type)
3595 {
3596     boolean featuresFile = false;
3597     try
3598     {
3599       featuresFile = new FeaturesFile(file,
3600           type).parse(viewport.alignment.getDataset(),
3601                                          alignPanel.seqPanel.seqCanvas.
3602                                          getFeatureRenderer().featureColours,
3603                                          false);
3604     }
3605     catch(Exception ex)
3606     {
3607       ex.printStackTrace();
3608     }
3609
3610     if(featuresFile)
3611     {
3612       viewport.showSequenceFeatures = true;
3613       showSeqFeatures.setSelected(true);
3614       alignPanel.paintAlignment(true);
3615     }
3616
3617     return featuresFile;
3618 }
3619
3620 public void dragEnter(DropTargetDragEvent evt)
3621 {}
3622
3623 public void dragExit(DropTargetEvent evt)
3624 {}
3625
3626 public void dragOver(DropTargetDragEvent evt)
3627 {}
3628
3629 public void dropActionChanged(DropTargetDragEvent evt)
3630 {}
3631
3632 public void drop(DropTargetDropEvent evt)
3633 {
3634     Transferable t = evt.getTransferable();
3635     java.util.List files = null;
3636
3637     try
3638     {
3639       DataFlavor uriListFlavor = new DataFlavor(
3640           "text/uri-list;class=java.lang.String");
3641       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3642       {
3643         //Works on Windows and MacOSX
3644         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3645         files = (java.util.List) t.getTransferData(DataFlavor.
3646             javaFileListFlavor);
3647       }
3648       else if (t.isDataFlavorSupported(uriListFlavor))
3649       {
3650         // This is used by Unix drag system
3651         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3652         String data = (String) t.getTransferData(uriListFlavor);
3653         files = new java.util.ArrayList(1);
3654         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3655             data,
3656             "\r\n");
3657              st.hasMoreTokens(); )
3658         {
3659           String s = st.nextToken();
3660           if (s.startsWith("#"))
3661           {
3662             // the line is a comment (as per the RFC 2483)
3663             continue;
3664           }
3665
3666           java.net.URI uri = new java.net.URI(s);
3667           java.io.File file = new java.io.File(uri);
3668           files.add(file);
3669         }
3670       }
3671     }
3672     catch (Exception e)
3673     {
3674       e.printStackTrace();
3675     }
3676     if (files != null)
3677     {
3678       try
3679       {
3680
3681         for (int i = 0; i < files.size(); i++)
3682         {
3683           loadJalviewDataFile(files.get(i).toString());
3684         }
3685       }
3686       catch (Exception ex)
3687       {
3688         ex.printStackTrace();
3689       }
3690     }
3691 }
3692
3693   // This method will attempt to load a "dropped" file first by testing
3694   // whether its and Annotation file, then features file. If both are
3695   // false then the user may have dropped an alignment file onto this
3696   // AlignFrame
3697    public void loadJalviewDataFile(String file)
3698   {
3699     try
3700     {
3701       String protocol = "File";
3702
3703       if (file.indexOf("http:") > -1 || file.indexOf("file:") > -1)
3704       {
3705         protocol = "URL";
3706       }
3707
3708       boolean isAnnotation = new AnnotationFile().readAnnotationFile(viewport.
3709           alignment, file, protocol);
3710
3711       if (!isAnnotation)
3712       {
3713         boolean isGroupsFile = parseFeaturesFile(file,protocol);
3714         if (!isGroupsFile)
3715         {
3716           String format = new IdentifyFile().Identify(file, protocol);
3717
3718           if(format.equalsIgnoreCase("JnetFile"))
3719           {
3720             jalview.io.JPredFile predictions = new jalview.io.JPredFile(
3721                 file, protocol);
3722             new JnetAnnotationMaker().add_annotation(predictions,
3723                 viewport.getAlignment(),
3724                 0, false);
3725             alignPanel.adjustAnnotationHeight();
3726             alignPanel.paintAlignment(true);
3727           }
3728           else
3729           {
3730             new FileLoader().LoadFile(viewport, file, protocol, format);
3731         }
3732       }
3733       }
3734       else
3735       {
3736         // (isAnnotation)
3737         alignPanel.adjustAnnotationHeight();
3738         buildSortByAnnotationScoresMenu();
3739       }
3740
3741     }
3742     catch (Exception ex)
3743     {
3744       ex.printStackTrace();
3745     }
3746   }
3747
3748   public void tabSelectionChanged(int index)
3749   {
3750     if (index > -1)
3751     {
3752       alignPanel = (AlignmentPanel) alignPanels.elementAt(index);
3753       viewport = alignPanel.av;
3754       setMenusFromViewport(viewport);
3755     }
3756   }
3757
3758   public void tabbedPane_mousePressed(MouseEvent e)
3759   {
3760     if(SwingUtilities.isRightMouseButton(e))
3761     {
3762       String reply = JOptionPane.showInternalInputDialog(this,
3763           "Enter View Name",
3764           "Edit View Name",
3765           JOptionPane.QUESTION_MESSAGE);
3766
3767       if (reply != null)
3768       {
3769         viewport.viewName = reply;
3770         tabbedPane.setTitleAt( tabbedPane.getSelectedIndex() ,reply);
3771       }
3772     }
3773   }
3774
3775
3776   public AlignViewport getCurrentView()
3777   {
3778     return viewport;
3779   }
3780
3781
3782   /**
3783    * Open the dialog for regex description parsing.
3784    */
3785   protected void extractScores_actionPerformed(ActionEvent e)
3786   {
3787     ParseProperties pp = new jalview.analysis.ParseProperties(viewport.alignment);
3788     if (pp.getScoresFromDescription("col", "score column ", "\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)\\W+([-+]?\\d*\\.?\\d*e?-?\\d*)")>0)
3789     {
3790       buildSortByAnnotationScoresMenu();
3791     }
3792   }
3793 }
3794
3795 class PrintThread
3796     extends Thread
3797 {
3798   AlignmentPanel ap;
3799   public PrintThread(AlignmentPanel ap)
3800   {
3801    this.ap = ap;
3802   }
3803   static PageFormat pf;
3804   public void run()
3805   {
3806     PrinterJob printJob = PrinterJob.getPrinterJob();
3807
3808     if (pf != null)
3809     {
3810       printJob.setPrintable(ap, pf);
3811     }
3812     else
3813     {
3814       printJob.setPrintable(ap);
3815     }
3816
3817     if (printJob.printDialog())
3818     {
3819       try
3820       {
3821         printJob.print();
3822       }
3823       catch (Exception PrintException)
3824       {
3825         PrintException.printStackTrace();
3826       }
3827     }
3828   }
3829 }