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