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